Objective-C - readonly properties not synthesized with underscore ivar?
Asked Answered
J

1

29

If I understand correctly, in Objective-C, properties are automatically synthesized with getters and setters, with the instance variable declared as the property name with an underscore prepended (_ivar).

So, this code:

main.m

#import <Foundation/Foundation.h>
#import "hello.h"

int main(int argc, char *argv[])
{

    @autoreleasepool {

        Hello *hello = [[Hello alloc] init];
        NSLog(@"%@", hello.myString);

        return 0;

    }

}

hello.h

#import <Foundation/Foundation.h>

@interface Hello : NSObject
@property (copy, nonatomic) NSString *myString;
@end

hello.m

#import "hello.h"

@implementation Hello

-(Hello *)init
{

    if (self = [super init]) {
        _myString = @"Hello";
    }

    return self;

}

-(NSString *)myString
{
    return [NSString stringWithFormat:@"%@ %@", _myString, @"World"];
}

@end

Can be compiled and run like this:

bash-3.2$ clang -framework Foundation main.m hello.m -o hello
bash-3.2$ ./hello
2013-05-27 13:20:39.738 hello[23320:707] Hello World

Now when I change the myString property to readonly like this:

@property (readonly, copy, nonatomic) NSString *myString;

Then when I try to compile I get an error:

hello.m:11:9: error: unknown type name '_myString'; did you mean 'NSString'?
        _myString = @"Hello";
        ^~~~~~~~~
        NSString

So _myString is not defined. Did the compiler not synthesize the property with instance variable _myString? Let's see if it works when I synthesize it myself:

In hello.m implementation:

@synthesize myString = _myString;

Now it works again:

bash-3.2$ clang -framework Foundation main.m hello.m -o hello
bash-3.2$ ./hello
2013-05-27 13:36:59.916 hello[24219:707] Hello World

So, my question is, why is it not automatically synthesized with an underscore ivar when you use readonly on properties? Or am I totally on the wrong path of understanding how this Objective-C stuff works?

I would very much appreciate an explaining answer, as I really want to understand what's exactly happening and why.

Thank you in advance.

Jasonjasper answered 27/5, 2013 at 11:41 Comment(0)
L
59

A property is not automatically synthesized if you implement all required accessor methods (getter for a read-only property, getter + setter for a read-write property).

Because you implement the getter method -(NSString *)myString for the read-only property, it is not auto-synthesized. If your getter needs an instance variable to backup the property value, you have to add the synthesize statement (as you did) or an instance variable.

Lobe answered 27/5, 2013 at 11:54 Comment(2)
I'll just add to that the probable justification for why Apple do it that way. Because if you implement both a getter and a setter (or just a getter for readonly properties), obj-c has no way of knowing that you intend to implement the property using an iVar, nor if you do the type of that iVar. And having an unused pointer iVar for example would waste 4 or 8 bytes for every object.Aney
Strictly speaking,Objective-C does not do the synthesis, the compiler does.Iman

© 2022 - 2024 — McMap. All rights reserved.