7

I have been learning Objective-C for awhile. From what I learned, I know that when you declare a variable inside @interface in the .h file, the variable can be accessed publicly(similar as a public variable in java).

@interface MyObject

@property NSInteger intData;

@end

But when you declare it inside @interface in the .m file. It can only be accessed inside the .m file under @implementation only, unless you provide a getter and setter for it.

@interface MyObject ()

@property NSInteger intData;

@end

But I also noticed another way of declaring a variable, which is declaring it under @implementation

@implementation

NSInteger intData;

@end

and I see that it works the same way as declaring it under @interface with @property in .m file

I don't understand the difference between the two(declaring under @implementation and under @interface(in .m file).

I've already searched through stack about this, but they were all talking about the difference between @implementation and @interface(in .h file). So think this is not a duplicate.

JLT
  • 3,052
  • 9
  • 39
  • 86
  • 3
    The `NSInteger` declared inside the `@interface` is a global variable. It's also just a variable, without any of the trappings of a property (accessors methods, KVC, etc.). – Rob Aug 08 '15 at 15:22

2 Answers2

6

First off, you're not declaring a variable; you're declaring a property. A property is backed by an instance-variable, but it also adds methods. Here's an explanation of the places to put variables:

@interface MyClass : NSObject {
    NSInteger i ;
}
@end

This is a place to put an instance variable on your class. It is only accessible by methods of your class and categories. (Sidenote: it CAN be made accessible externally, but that's not a recommended practice)

Another example:

@interface MyClass : NSObject
@end

@implementation MyClass {
    NSInteger i ;
}
@end

This is also an instance variable, but is only accessibly by methods written inside that block. (Sidenote: it can be accessed by digging through the class definition, but that's not a recommended (or common) practice)

Another example:

@interface MyClass : NSObject
@property NSInteger i ;
@end

Is the same as:

@interface MyClass : NSObject {
    NSInteger _i ; // you are not allowed to access it by this variable
}
- (NSInteger) i ;
- (void) setI:(NSInteger)value ;
@end

This is a property people are allowed to get and set. You use that variable in your methods or in other methods as:

NSLog ( @"The value is %i" , self.i ) ; // if it's your instance method
NSLog ( @"The value is %i" , object.i ) ; // if it's object's instance method

Another example:

@interface MyClass : NSObject {
    NSInteger i ;
}
@property NSInteger i ;
@end
@implementation MyClass
@synthesize i ; // Causes the property to line up with the ivar by the same name.
@end

Is the same as:

@interface MyClass : NSObject {
    NSInteger i ; // you ARE allowed to use this since you defined it
}
- (NSInteger) i ;
- (void) setI:(NSInteger)value ;
@end

Here, you can use the getter/setter methods or the instance variable itself. However, you should generally use the methods because you [implicitly] declared them atomic so they have threading synchronization. If you want to make it NOT do threading (and speed it up, as long as you're not going to use it in a multi-threaded environment):

@property (nonatomic) NSInteger i ;
@property (nonatomic,readonly) NSInteger i ; // only makes a getter method

I'd recommend avoiding this for a while and use the straight properties because it'll help you avoid a lot of common mistakes. Unless you profile your program and determine that this is a cause of a performance loss, you should probably simply use the properties.

Another example:

@interface MyClass : NSObject
@end

@implementation MyClass
NSInteger i ;
@end

This is NOT an instance variable. It is a global variable that happens to have been written inside your @implementation scope.

See above for how to turn this into an instance variable (i.e. putting it in braces).

One more note:

Declaring a property like this:

@interface MyClass ()
@property NSInteger i ;
@end

Doesn't make it private. However, it is hidden in a file that people generally can't access so the compiler doesn't know a property exists.

Other functions elsewhere in your code CAN still call:

[yourObject i] ;

To get the value of that property - but they have to know it's there first.

Addendum to answer a question in the comments:

Properties are, by default, atomic. It doesn't necessarily follow the strict definition of atomic (this is a can of worms I suggest you not look at right now), but has the same effect: threads are guaranteed to see a complete and up-to-date value, regardless of when another thread writes to it. It generally does this when it synthesizes the getter/setter methods:

- (NSInteger) i {
    @synchronized(self) {
        return i ;
    }
}
- (void) setI:(NSInteger)value {
    @synchronized(self) {
        i = value ;
    }
}

If you instead specify nonatomic, it'll synthesize these:

- (NSInteger) i {
    return i ;
}
- (void) setI:(NSInteger)value {
    i = value ;
}

If your property is atomic, then you shouldn't ever access the ivar directly. Doing so violates the threading protection you gave it to begin with. (Sidenote: there are cases where you can, but wait until you become more familiar with threading/synchronization before you attempt it.)

iAdjunct
  • 2,739
  • 1
  • 18
  • 27
  • First of all, thank you for making such an effort to answer my question. I just want to clarify some things in your answer. In your explanation, the part starting with "Here, you can use getter and setter methods...", you mentioned that "you should generally use the methods because you[implicitly] declared them atomic". Uhm, did you mean "nonatomic"? because your example after that states @property (nonatomic) NSInteger i. I am a little confused about that part. – JLT Aug 08 '15 at 15:43
  • 1
    @EpicNinja - I added some extra stuff at the bottom describing what `atomic` and `nonatomic` are doing. – iAdjunct Aug 08 '15 at 17:12
  • 2
    A few problems with this answer. 1) The 1st code sample shouldn't be used. There is no reason to ever declare private ivars in the public header file. 2) The 2nd code sample declares a private ivar. Your statement "accessibly by methods written inside that block" is a bit confusing. Such a variable can be accessed by any instance method of the class. 3) You should mention that the global variable declaration inside the `@implementation` line can be made into a proper ivar simply by using curly braces. – rmaddy Aug 08 '15 at 18:01
  • 1
    "Other functions elsewhere in your code CAN still call: `[yourObject i] ;` To get the value of that property - but they have to know it's there first." To be completely clear, the _compiler_ will enforce the privacy of this property: it will not allow you to call the method in a place that does not import the declaration. – jscs Aug 08 '15 at 18:11
  • @JoshCaswell - not necessarily. There are numerous ways to do that (declaring another category yourself for you use that has the method; inspecting the class definition; calling [object performSelector:@selector(i)]; etc). The compiler will enforce the ordinary cases where it's not visible, but that's more "hidden" than it is "private". – iAdjunct Aug 08 '15 at 18:17
  • @rmaddy - (1) There is a reason: when you're also working with C++ developers who want a clear declaration/definition split. (2) It cannot because, if that `@implementation` block is in a .m file, another .m file defining a category cannot see that variable (unless the compiler has gotten REALLY smart). (3) Edited. – iAdjunct Aug 08 '15 at 18:19
  • 2) Is that what you meant by "block" in your answer? Just the same .m file? I agree about the scope being the same .m but using the word "block" is far less clear than stating "methods in the same .m file". – rmaddy Aug 08 '15 at 18:22
  • @rmaddy - to be honest, I'm not 100% sure what the scope of that is (same file vs. `@implementation`/`@end`) so I left it like that. However, it's rare to be putting multiple `@implementation` blocks in the same file, so the difference is moot until you start doing more creative things. – iAdjunct Aug 08 '15 at 18:26
4

When you declare the property in the @interface MyObject (){} you are declaring it in what's called an annonymous or class category. Because it's declared in the .m file, it's only visible within that file (no other classes can see it). However, you could have just as well declared this category in your .h file, in which case, it would be visible to other classes.

Declaring NSInteger intData inside the @implementation part of your class isn't actually declaring an instance variable. It's declaring a global variable, which means there is a single instance of it that is shared by your whole application, or, if you'd like to look at it this way, all instances of your class (since it is the only one aware of this global variable).

Arie Litovsky
  • 4,893
  • 2
  • 35
  • 40
  • 3
    "called an [anonymous] or class category" ... Or a "class extension" (see [Class Extensions Extend the Internal Implementation](https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/CustomizingExistingClasses/CustomizingExistingClasses.html#//apple_ref/doc/uid/TP40011210-CH6-SW3)). – Rob Aug 08 '15 at 15:33
  • 1
    The global variable can be made into an ivar simply by adding curly braces. – rmaddy Aug 08 '15 at 18:02