2012-11-16 47 views
1

隨着後續各種各樣的到Is returning nil from a [[class alloc] init] considered good practice?,還有,我還沒有看到任何討論比較多的情況下:做什麼用的,以前失敗的一些前提條件一個init可以調用下一個init?跟帖從返回nil [類的alloc]初始化]

例如,假設在此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]; 
} 

也許更清楚的例子是,如果我們包裹指定初始化方法採用一個對象的指針,並拋出如果其通過零個例外。我們肯定需要將導致異常的init調用短路。

我的猜測:init以任何方式可能,只有在返回nil之前釋放自己。如有必要,在釋放之前,調用裸啓動器或任何其他初始化器,以使自己進入已知狀態。

// 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影響的情況呢?

我的猜測:仍然以任何可能的方式完成init,然後返回nil。你會認爲設置自我可能是多餘的,但在某些情況下並非如此。在任何情況下,它都需要在那裏靜默編譯器警告。

// 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; 

我的猜測是錯誤的嗎?

回答

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之前檢查指針),那麼你可以不安全地調用[super init]

如果知道你的超類的dealloc是安全的,那麼我的建議是,你移動你的前提出來的init和成類工廠方法。

換句話說,不要把alloc/init作爲你班級的公共接口的一部分。提供創建實例的類方法:

// The class factory method. Declare this in your header file. This is how you 
// or any user of this class should create instances. 
+ (id)myObjectWithStuff:(Stuff *)stuff { 
    if (!stuff || ![stuff isValid]) 
     return nil; 

    // self here is the class object, so it's appropriate to send `alloc` to it. 
    // You don't want to hardcode the class name here because that would break 
    // subclassing. 
    return [[self alloc] initWithStuff:stuff]; 
} 

// This is now considered a private method. You should not declare it in your 
// header file, though in Objective-C you can't prevent the user from calling it 
// if he's determined to. 
- (id)initWithStuff:(Stuff *)stuff { 
    // Precondition was already checked in myObjectWithStuff:. 
    if (self = [super init]) { 
     // initialization here... 
    } 
    return self; 
} 
+0

這是如何不泄漏alloc_d內存的可能對象? –

+0

@smallduck我想到了這一些,並修改了我的答案。 –