Questions about the designated initializer in the book


#1

I have 3 questions about the designated initializer code on pages 49 and 50 of the 3rd edition book:

  1. Page 49: If self is “automatically set to point at the object that was sent the message”, then doesn’t the line of code self = [super init] wipe out the self pointer/replace it with what [super init] returns? Would the code be the same if you did not assign the return value of [super init] to self?

  2. Page 49: To initialize the instance variables, why not just do this: itemName = name; Why call the itemName settor on self?

  3. Page 50: Why even test “if (self)”? Even if [super init] returns nil, you don’t need to perform the test before you call the settors to initialize the instance variables, because sending a message to nil does nothing anyway. Correct? So is testing if self is nil only there for performance reasons?

Thanks,
Chris


#2

Yes.

No.

To find out why, you should read: The Objective-C Programming Language in Xcode’s documentation set, particularly the section: Allocating and Initializing Objects.

Also see the example I have concocted:

//  main.m

#import <Foundation/Foundation.h>

@interface Bar: NSObject
- (id)initWithSurprise;
- (void)work;
@end

@interface FooBar: Bar
- (id)initWithSurprise;
@end

#define MY_log() NSLog (@"%s: %@", __PRETTY_FUNCTION__, [self className])
#define MY_logP(P1) NSLog (@"%s: %@: %p", __PRETTY_FUNCTION__, [self className], P1)

int main (int argc, const char * argv[])
{
    @autoreleasepool {
        
        FooBar *fooBar1 = [[FooBar alloc] init];
        FooBar *fooBar2 = [[FooBar alloc] initWithSurprise];
        
        [fooBar1 work];
        [fooBar2 work];

    }
    return 0;
}

@implementation Bar

- (id)initWithSurprise
{
    MY_logP (self);
    self = [[self class] alloc];
    MY_logP (self);

    return self;
}

- (void)work
{
    
}

- (void)dealloc
{
    MY_log();
}

@end

@implementation FooBar

- (id)init
{
    MY_logP (self);
    self = [super init];
    MY_logP (self);
    return self;
}

- (id)initWithSurprise
{
    MY_logP (self);
    self = [super initWithSurprise];
    MY_logP (self);
    return self;
}

- (void)dealloc
{
    MY_log();
}
@end

The output:

2012-07-30 12:18:25.406 crizzola[72384:503] -[FooBar init]: FooBar: 0x7fa668c142a0
2012-07-30 12:18:25.408 crizzola[72384:503] -[FooBar init]: FooBar: 0x7fa668c142a0
2012-07-30 12:18:25.409 crizzola[72384:503] -[FooBar initWithSurprise]: FooBar: 0x7fa668c021e0
2012-07-30 12:18:25.409 crizzola[72384:503] -[Bar initWithSurprise]: FooBar: 0x7fa668c021e0
2012-07-30 12:18:25.409 crizzola[72384:503] -[FooBar dealloc]: FooBar
2012-07-30 12:18:25.410 crizzola[72384:503] -[Bar dealloc]: FooBar
2012-07-30 12:18:25.410 crizzola[72384:503] -[Bar initWithSurprise]: FooBar: 0x7fa668e00080
2012-07-30 12:18:25.411 crizzola[72384:503] -[FooBar initWithSurprise]: FooBar: 0x7fa668e00080
2012-07-30 12:18:25.411 crizzola[72384:503] -[FooBar dealloc]: FooBar
2012-07-30 12:18:25.412 crizzola[72384:503] -[Bar dealloc]: FooBar
2012-07-30 12:18:25.412 crizzola[72384:503] -[FooBar dealloc]: FooBar
2012-07-30 12:18:25.413 crizzola[72384:503] -[Bar dealloc]: FooBar

#3

Yes, you can do that. The authors just prefer using message syntax.

[quote]3. Page 50: Why even test “if (self)”? Even if [super init] returns nil, you don’t need to perform the test before you call the settors to initialize the instance variables, because sending a message to nil does nothing anyway. Correct? So is testing if self is nil only there for performance reasons?
[/quote]
If all the messages in the code block were simply assignments to instance variables in self, you are correct, there would be no real requirement to check if self was nil. However, not checking for nil in the book’s code would cause a problem because the code block includes the creation of an NSDate object which is then assigned to the dateCreated property. If this code is executed when self is nil, I suspect you would get an exception or, at best, a floating NSDate object with no way to reference it, thereby creating a memory leak.


#4

Yes, you can do that. The authors just prefer using message syntax.

[/quote]
But actually there is a difference if you do that outside the initialiser.

// This will not trigger a KVO message
itemName = name;

// these are equivalent; they will trigger a KVO message.
[self setItemName:name];
self.itemName = name;

Directly setting the value of an instance variable suppresses KVO notifications:

//  main.m

#import <Foundation/Foundation.h>

#define MY_log() NSLog (@"%s", __PRETTY_FUNCTION__)
#define MY_logs(P1) NSLog (@"%s: %s", __PRETTY_FUNCTION__, P1)
#define MY_log1(P1, P2) NSLog (@"%s: "P1, __PRETTY_FUNCTION__, P2)

@interface Foo:NSObject

@property NSInteger fooness;

- (void)setFoonessByStealth:(NSInteger)v;

@end

@interface Bar:NSObject

@property Foo *observedFoo;

- (id)initWithFoo:(Foo *)foo;

@end

int main (int argc, const char * argv[])
{
    @autoreleasepool {
        Foo *foo = [[Foo alloc] init];
        Bar *bar = [[Bar alloc] initWithFoo:foo];
        
        MY_logs ("By stealth...");
        [foo setFoonessByStealth:-1];
        MY_logs ("By stealth: done.");

        [foo setFoonessByStealth:0];
        [foo setFoonessByStealth:1];

        MY_logs ("Trigger KVO...");
        foo.fooness = -1;
        foo.fooness = 0;
        foo.fooness = 1;
        MY_logs ("Trigger KVO: done.");
        
        foo = nil;
        bar = nil;
    }
    return 0;
}

@implementation Foo

@synthesize fooness;

- (void)setFoonessByStealth:(NSInteger)v
{
    fooness = v;
}
@end

@implementation Bar

@synthesize observedFoo;

- (id)initWithFoo:(Foo *)foo
{
    if (self = [super init])
    {
        self.observedFoo = foo;
        [foo addObserver:self forKeyPath:@"fooness" options:0 context:nil];
    }
    return self;
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    MY_log1 ("%@", object);
}

- (void)dealloc
{
    MY_log();
    [observedFoo removeObserver:self forKeyPath:@"fooness"]; 
}
@end

The output:

2012-08-02 21:06:10.456 calwas[81549:503] int main(int, const char **): By stealth...
2012-08-02 21:06:10.458 calwas[81549:503] int main(int, const char **): By stealth: done.
2012-08-02 21:06:10.458 calwas[81549:503] int main(int, const char **): Trigger KVO...
2012-08-02 21:06:10.459 calwas[81549:503] -[Bar observeValueForKeyPath:ofObject:change:context:]: <Foo: 0x108e141a0>
2012-08-02 21:06:10.459 calwas[81549:503] -[Bar observeValueForKeyPath:ofObject:change:context:]: <Foo: 0x108e141a0>
2012-08-02 21:06:10.460 calwas[81549:503] -[Bar observeValueForKeyPath:ofObject:change:context:]: <Foo: 0x108e141a0>
2012-08-02 21:06:10.460 calwas[81549:503] int main(int, const char **): Trigger KVO: done.
2012-08-02 21:06:10.461 calwas[81549:503] -[Bar dealloc]

#5

ibex: Thanks for pointing out that advanced and subtle difference.