作为对Is returning nil from a [[class alloc] init] considered good practice?的分类的跟进,有一种情况我没有看到任何讨论过多的问题:如何处理一个在调用下一个init之前失去一些先决条件的init?
示例,假设在此initWithStuff:方法中传递nil或者通常没有值传递给initWithValue:绝对失败,我们肯定想返回nil。
- (id)initWithStuff:(Stuff *)inStuff {
if (!inStuff || ![inStuff hasValidValue])
{
// can't proceed to call initWithValue: because we have no value
// so do what?
return nil;
}
NSInteger value = [inStuff integerValue];
return [super initWithValue:value];
}
也许一个更清晰的例子是,如果我们包装的指定初始化方法接受一个对象指针,并且如果它传递了nil则抛出异常。我们肯定需要将导致异常的init调用短路。
我的猜测:以任何可能的方式初始化,然后在返回nil之前释放self。如有必要,请调用裸init或任何其他初始化程序,以便在释放之前完成将self置于已知状态。
// can't proceed to call super's initWithValue: because we have no value
// so do what? do this:
self = [super init]; // or initWithValue:0
[self release];
return nil;
如果没有这样的初始化器可以在没有有效数据的情况下工作,我想有人需要构建一些有效的伪数据。或者向其作者抱怨,直到那时只返回零并与泄漏一起生活:^)
此外,ARC如何影响这种情况?
我的猜测:仍然以任何可能的方式完成初始化,然后返回零。你认为设置自我可能是多余的,但在某些情况下并非如此。在任何情况下,它都需要在那里使编译器警告静音。
// can't proceed to call super's initWithValue: because we have no value
// so do what? do this:
self = [super init]; // finish init so ARC can release it having no strong references
return nil;
我的猜测是否有任何错误?
答案 0 :(得分:1)
理想情况下,如果前提条件失败,则不要调用[super init…]
。您只需发布self
(如果不使用ARC)并返回nil:
- (id)initWithStuff:(Stuff *)stuff {
if (!stuff || ![stuff isValid]) {
[self release]; // if not using ARC
return nil;
}
if (self = [super init]) {
// initialization here
}
return self;
}
该版本负责在MRC下解除分配self
。在ARC下,编译器将为您插入发行版。
但是,此方法存在潜在问题。当您发布self
时(或当ARC为您发布)时,系统将发送dealloc
消息对象。您的dealloc
方法会调用[super dealloc]
。您可以取消MRC下的[super dealloc]
,但无法通过ARC来避免它。
因此,危险在于您的超类可能会假设其实例变量之一已初始化,并依赖其dealloc
中的初始化值。例如,假设这是超类:
@interface SomeSuperclass : NSObject
@end
@implementation SomeSuperclass {
CFMutableBagRef bag;
}
- (id)init {
if (self = [super init]) {
bag = CFBagCreateMutable(NULL, 0, &kCFTypeBagCallBacks);
}
return self;
}
- (void)dealloc {
CFRelease(bag);
}
@end
这里的问题是CFRelease
要求其参数不是零。因此,如果不在子类中调用[super init]
,这将在解除分配期间崩溃。
鉴于这个问题,我必须改变我的初步建议。如果你知道你的超类dealloc
没有这种问题(例如,因为它在取消引用它们或将它们传递给CFRelease
之前检查指针),那么你就可以放心地调用{{ 1}}。
如果您不知道您的超类[super init]
是安全的,那么我的建议是将您的前置条件从dealloc
移出并进入班级工厂方法。
换句话说,不要将init
视为班级公共界面的一部分。提供用于创建实例的类方法:
alloc/init