0

I've been writing Objective-C for a few years now, and decided to go back and learn the very basics to help me write even better code. I'm trying to learn all about instance variables, inheritance and class extensions. I've been reading up on all three, but there is one thing that boggles my mind. I have a simple app that contains 2 classes, Person, Male (inherits from Person), and of course Main (which imports the Male class, therefore being able to access the instance variables found in both Person and Male).

The code is simple, and for the sake of space I won't post all of it. Basically Main takes these variables and plays around with them. This is the part that is boggling my mind:

    @interface Person : NSObject {
    float heightInMeters;
    int weightInKilos;
}

@property float heightInMeters;
@property int weightInKilos;

@end

When I delete the brackets and variable declarations, leaving it like this:

@interface Person : NSObject

@property float heightInMeters;
@property int weightInKilos;

@end

The code still inherits and executes just fine.

1. What is the point of even declaring them there in the first place if we can just create two properties?

2. why create two instance variables AND properties to correspond with them?

3. I know that we can declare the variables in the .m instead to keep them private to the class and everything that subclasses it. like this:

    @implementation Person {
    float heightInMeters;
    int weightInKilos;
    }

What is the difference here? I feel like I'm missing a lot of basics. Is there a simplistic way of putting this all in perspective?

Henry F
  • 4,960
  • 11
  • 55
  • 98
  • 1
    "Automatic property synthesis" was added in Xcode 4.4, so your second version is fine, and you don't have to declare the instance variables in most cases. See for example http://stackoverflow.com/questions/9368676/under-what-conditions-is-synthesize-automatic-in-objective-c. – Martin R Oct 31 '14 at 18:03
  • @MartinR That's interesting... thank you for the example. And on a somewhat related note of confusion... heightInMeters and weightInKilos are both instance variables as opposed to class variables, correct? – Henry F Oct 31 '14 at 18:06
  • 1
    Yes. To be precise, if you declare `@property float heightInMeters;` then the compiler will "synthesize" a corresponding instance variable `_heightInMeters` (with the underscore prepended). But there are already *a lot of* Q&A around that topic, if you search for "property vs instance variables" you should find everything you need. – Martin R Oct 31 '14 at 18:09
  • Also non-fragile instance variables were added in Objective-C 2.0. This eliminated the need to declare everything in the @interface { } block – KirkSpaziani Oct 31 '14 at 20:15

1 Answers1

2

When you declare a @property, the compiler will automatically synthesize the variable prefixed with an underscore, a getter method, and a setter method.

@interface MyClass () 

@property(strong, nonatomic) NSString *myString;

@end

In this example the compiler would syhtnesize the variable as _myString, the getter as

-(NSString *)myString

and the setter as

-(void)setMyString:(NSString *)string

The keywords after "@property" (strong, nonatomic) define the property's attributes. strong, the default, implies ownership, meaning that in this case MyClass instances will essentially be responsible for the retain/release of their respective myString objects. nonatomic means the variable is not guaranteed to always be a valid value in a multithreaded environment, for example if the getter is called at the same time as the setter.

Additionally, the compiler will treat dot syntax used to retrieve/set instance variables as calls to the appropriate getter/setter methods. Therefore, given an instance of MyClass

MyClass *exampleClass = [[MyClass alloc] init];

Both of the following are equivalent statements:

NSString *string1 = example.myString;   // dot syntax
NSString *string1 = [example myString]; // explicit call to the getter method

For further reading, take a look at Apple's Programming with Objective-C Guide.

As for your specific questions:

1. What is the point of even declaring them there in the first place if we can just create two properties?

It's actually not a good idea to declare variables explicitly as public variables in your MyClass.h file (or in most other cases). Instead, declaring them as properties automatically creates a private variable (and accessor methods), making adhering to OOP best practices a little easier. So there is no point in declaring

// MyClass.h

@interface MyClass : NSObject {
    NSString *myString // public variables not good
}

Also because of what I stated above regarding dot syntax, if you use self.myString internally in MyClass.m or instanceOfMyClass.myString externally, the public variable myString will never even be touched because the synthesized variable is named _myString.

2. Why create two instance variables AND properties to correspond with them?

See above--you don't need two instance variables, only one.

3. I know that we can declare the variables in the .m instead to keep them private to the class and everything that subclasses it. What is the difference here? I feel like I'm missing a lot of basics. Is there a simplistic way of putting this all in perspective?

If you declare your variables privately in the @implementation part of your .m file, the compiler won't be able to help you by synthesizing the getters and setters. Even as private methods, getters and setters can help reduce complexity in your code, for example checking for the validity of variable values. (Note: you can override accessor methods.)

// MyClass.m

@interface MyClass () // private interface

@property(nonatomic, strong) NSString *myString;

@end


@implementation MyClass {
    // no more need for private variables!
    // compiler will synthesize NSString *_myString and accessors
}

-(void)setMyString:(NSString *)string { // overwrite setter

    // no empty strings allowed in our object (for the sake of example)
    NSAssert([string length] > 0, @"String must not be empty");

    // assign private instance variable in setter
    _myString = string;
}

@end

This way, even when you subclass MyClass, the subclass will inherit the getter and setter methods that were synthesized for us by the compiler.

Community
  • 1
  • 1
Morgan Chen
  • 1,137
  • 7
  • 18
  • Thank you for the help, it provides great insight. I also bolded three questions specific to instance variables and class extensions, would you be able to provide any insight with those? – Henry F Oct 31 '14 at 19:21
  • You are awesome, thank you so much! Extremely helpful. So, the only reason to declare private variables in the .m is mostly just if you want to manually create the getters and setters, right? – Henry F Oct 31 '14 at 20:02
  • No, you can recreate the getters and setters for a property declared in either file. Typically properties declared as `readonly` in the `.h` file will then be redeclared as `readwrite` in the `.m`. The compiler won't synthesize a setter for a `readonly` property, so redeclaring it in the `.m` allows a private setter to be synthesized for internal use. There are more tricks, and many a blog post has been written on this subject. – Morgan Chen Oct 31 '14 at 20:06