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?
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With