Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift super initializer

Tags:

swift

In Swift, super's initializer should be called after all properties of the current class have been initialized. This is however not done for Objective-C init, where super init is called first before initializing properties in the current class.

What issues is Swift trying to prevent by enforcing this? Why is Objective-C able to avoid the issues Swift is trying to prevent?

like image 442
Boon Avatar asked Sep 01 '14 01:09

Boon


2 Answers

What issues is Swift trying to prevent by enforcing this?

This is a great question, and Objective-C did not avoid it.

The problem is that while you're inside an initializer method, the object is technically in a partially constructed state. Bryan's post is a great (albeit contrived) example of why. The general issue is that if a super class's initializer invokes a method, a subclass may have overridden this method. That, in and of itself, is not a bad thing. The problem arises if the overridden method assumes that the object is totally constructed.

However, since the object is still in the midst of invoking the initializers, that is not the case. The object is not wholly constructed until the call to [super init] returns and the class of the object executes any of its initialization code.

There's a related problem with dealloc methods: if you invoke methods inside your -dealloc method, those methods may assume that the object is wholly constructed, when in fact it may be partially deconstructed. This isn't as big of a deal under ARC, but it can still lead to some very subtle bugs.

With Swift, the decision was made to avoid these class of problems by enforcing this rule:

By the time you decide to call super, the calling class must have finished any class-specific initialization.

A variant of this rule is:

You may not invoke methods until after you have called super's initializer.

With this rule, you will never run into the problem described above.

like image 94
Dave DeLong Avatar answered Sep 28 '22 05:09

Dave DeLong


ObjC does not avoid anything.

For this ObjC code, it crashed because parent class is trying to access ivar from child class. It can be detected/avoid if the Swift rule is used. i.e. initialize all members before [super init]

@interface Parent : NSObject

@property (readonly) int value;

@end

@implementation Parent

- (id)init {
    self = [super init];
    if (self) {
        NSLog(@"%d", self.value); // call a method, which can be overrided by child class
    }
    return self;
}

- (int)value {
    return 42;
}

@end

@interface Child : Parent

@end

@implementation Child {
    int *_valuePtr;
}

- (id)init {
    self = [super init]; // call self.value
    if (self) {
         // to avoid crash, move this line before [super init], but it may have other undesired effect. e.g. when [super init] return another instance
        _valuePtr = calloc(sizeof(int), 1); 
    }
    return self;
}

- (void)dealloc {
    free(_valuePtr);
}

- (int)value {
    return *_valuePtr;
}

- (void)setValue:(int)value {
    *_valuePtr = value;
}

@end
like image 37
Bryan Chen Avatar answered Sep 28 '22 05:09

Bryan Chen