To self or not to self


#1

In the following code from the designated initializer for the BNRMapPoint class

[code]- (id)initWithCoordinate:(CLLocationCoordinate2D)c title:(NSString *)t {
self = [super init];

if (self) {
    coordinate = c;
    [self setTitle:t];
}

return self;   

}[/code]

Why isn’t the line

written as

My understanding of self is that it ensures we are using the synthesized getters and setters rather than the backing ivar - although there is probably a much better explanation than that. But why the discrepancy here ? Is the line

accessing the backing ivar or am I misunderstanding something? Don’t be afraid of saying harsh things about my lack of understanding.

Cheers,

Gav


#2

@spikeygoat It’s because coordinate is declared as a readonly property:

// This is a required property from MKAnnotation @property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
Only the getter will work in this situation. Sending the setter to self will throw an “unrecognized selector sent to instance” error and crash your program.

As described in the book on page 117, even though coordinate is a required property in the MKAnnotation protocol, you don’t have to declare it as readonly. The protocols are only dictated by method signatures. You can alter the property attribute:

// This is a required property from MKAnnotation @property (nonatomic) CLLocationCoordinate2D coordinate;
Then alter the initWithCoordinate:title: method:

- (id)initWithCoordinate:(CLLocationCoordinate2D)c title:(NSString *)t { self = [super init]; if (self) { //coordinate = c; [self setCoordinate:c]; [self setTitle:t]; } return self; }
And it will work.


#3

@BryanLuby

Thanks for that. Your explanation makes perfect sense. I confess I hadn’t properly understood that the method signatures could be changed for a required property and your code example does indeed compile. Am I right in thinking then that the code as laid out in the book :

[code]- (id)initWithCoordinate:(CLLocationCoordinate2D)c title:(NSString *)t {
self = [super init];

if (self) {
    coordinate = c;
    [self setTitle:t];
}

return self;   

}[/code]

is indeed accessing the backing ivar (something which I thought was a no no apart from in the accessor methods) to work round the problem that the property is a read only property.

Many thanks,

Gav


#4

@spikeygoat I think you are correct. Unless I am wrong, in the code:

coordinate = c;You are setting the instance variable.

And in the code:

[self setCoordinate:c];You are setting the property.

Since the property attribute was declared as readonly, we set the instance variable instead as a workaround.

A different way would be to keep the readonly property attribute in your public .h file. Then add a private class extension to the .m file with the property attribute as readwrite. This would generate a setter only for this class:
BNRMapPoint.m

[code]@interface BNRMapPoint ()

@property (nonatomic, readwrite) CLLocationCoordinate2D coordinate;
// I specified readwrite in the code for clarity. Leaving it blank also
// sets the attribute to readwrite by default.

@end

@implementation BNRMapPoint[/code]This would enable you to use the setter inside the initializer.

Does anyone know which is the best way for this situation? Does it matter? What are the best practices for using properties versus ivars inside of initializers?


#5

@BryanLuby

Thanks for the reply. I hadn’t considered adding a private extension to the implementation file. My initial reason for starting the “to self” discussion was because of the dichotomy in the code presented in the book.

I’m not sure of the ethics of presenting another source of iOS learning in this forum but I shall preface it by saying that this book and the following course are the best two resources I’ve come across. This from a man who has bought every (and that is no exaggeration) book on the subject.

Anyways. In this course (http://itunes.apple.com/gb/course/ipad-iphone-app-development/id495052415).They explicitly say to stay away from accessing the ivars apart from in the accessor methods. They also recommend synthesizing your properties with a different name for the ivars such as
this:

@implementation BNRMapPoint @synthesize coordinate = _coordinate; @synthesize title = _title;

And whilst this wouldn’t solve the issue that you’ve so readily highlighted about a read only property it would in my (albeit tiny) mind generate code consistency - something that is so useful when learning. To me the use of “self” is a handholder that I’m using the property and not the ivar.

This is definitely NOT a whinge about this book it’s just an outpouring of (now defused) confusion.

Cheers,

Gav


#6

A few thoughts on the style presented by the other course, since there is a lot of debate in the community around these two issues.

It used to be that you would want to use setters everywhere because of manual reference counting. However, now, ARC means we can just assign a raw instance variable to point at a new object, and everything works just like the setter method does. I still use the setter method unless the setter method has extra behavior in it that I’m trying to avoid. Sometimes, I don’t want a setter method for a particular instance variable, and in that case, you have to access the ivar directly (as shown here). When it comes to using a value stored in an instance variable from within the class, I typically just access the ivar directly instead of use the getter… it doesn’t really matter.

As to the prefixing ivars with underscores, I hate this, but Apple is slowly adopting it. The idea is that when people use dot syntax, enough programmers make the mistake of confusing these two statements:

myIvar = x;
self.myIvar = x;

Where the first line bypasses the setter and the second does not. In order to cut down on this mistake, people have been prefixing their ivars with _ because it’s a bit harder to type and a bit more obvious to the eyes. So now, you have this:

// Valid
_myIvar = x;
self.myIvar = x;

// Invalid
myIvar = x;
self._myIvar = x;

Of course, after enough time of using this prefix, it will become muscle memory for these developers and they will accidentally access ivars directly again. We don’t use this style of underscore prefixing, but it’s becoming popular among dot syntaxers.