在Swift中,应该在初始化当前类的所有属性之后调用super的初始值设定项。但是,Objective-C init没有这样做,在初始化当前类的属性之前首先调用super init。
Swift试图通过强制执行此操作来阻止哪些问题?为什么Objective-C能够避免Swift试图阻止的问题?
答案 0 :(得分:10)
Swift试图通过强制执行此操作来阻止哪些问题?
这是一个很好的问题,Objective-C没有避免它。
问题在于,当您在初始化方法中时,该对象在技术上处于部分构造状态。 Bryan's post是一个伟大的(尽管做作的)原因的例子。一般问题是,如果超类的初始化程序调用方法,则子类可能已重写此方法。这本身并不是一件坏事。如果重写的方法假设该对象是完全构造的,则会出现问题。
但是,由于对象仍在调用初始值设定项中,因此情况并非如此。在对[super init]
的调用返回并且对象的类执行其任何初始化代码之前,该对象并不是完全构造的。
dealloc
方法存在相关问题:如果在-dealloc
方法中调用方法,那些方法可能会假定对象是完全构造的,而实际上它可能是部分解构的。这在ARC下并不是一件大事,但它仍然会导致一些非常微妙的错误。
使用Swift,决定通过执行此规则来避免这类问题:
当你决定调用
super
时,调用类必须完成任何特定于类的初始化。
此规则的变体是:
在调用
super
的初始化程序之后,您才可以调用方法。
使用此规则,您将永远不会遇到上述问题。
答案 1 :(得分:5)
ObjC没有避免任何事情。
对于这个ObjC代码,它崩溃了,因为父类试图从子类访问ivar。如果使用Swift规则,则可以检测/避免它。即在[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