我從關於著名Appledoc閱讀本(或臭名昭著的?)init
方法在init方法中取代自己是一種不好的做法嗎?
在某些情況下,init方法可能會返回一個替代對象。因此,您必須在隨後的代碼中始終使用由init返回的對象,而不是由alloc或allocWithZone:返回的對象。
所以說我有這兩個類
@interface A : NSObject
@end
@interface B : A
@property (nonatomic, strong) NSArray *usefulArray;
@end
與以下實現
@implementation A
+(NSMutableArray *)wonderfulCache {
static NSMutableArray *array = nil;
if (!array)
array = [NSMutableArray array];
return array;
}
-(id)init {
if (self=[super init]) {
// substituting self with another object
// A has thought of an intelligent way of recycling
// its own objects
if ([self.class wonderfulCache].count) {
self = [self.class wonderfulCache].lastObject;
[[self.class wonderfulCache] removeLastObject];
} else {
// go through some initiating process
// ....
if (self.canBeReused)
[[self.class wonderfulCache] addObject:self];
}
}
return self;
}
-(BOOL) canBeReused {
// put in some condition
return YES;
}
@end
@implementation B
-(id)init {
if (self=[super init]) {
// setting the property
self.usefulArray = [NSArray array];
}
return self;
}
@end
當B調用init
,該[super init]
可能會返回一個取代的目的,並且不會吧當B嘗試設置屬性(A沒有)時會導致錯誤?
如果這確實會導致錯誤,我們如何才能以正確的方式實現上述模式?
更新:將一個更真實的特定問題
這裏的所謂C
C++類(其使用將在後面解釋)
class C
{
/// Get the user data pointer
void* GetUserData() const;
/// Set the user data. Use this to store your application specific data.
void SetUserData(void* data);
}
說的A
的目的是充當一個包裝的C
;在A
和C
之間始終保持一對一關係至關重要。
於是我想出了以下inteface和實施
@interface A : NSObject
-(id)initWithC:(C *)c;
@end
@implementation A {
C *_c;
}
-(id)initWithC:(C *)c {
id cu = (__bridge id) c->GetUserData();
if (cu) {
// Bingo, we've got the object already!
if ([cu isKindOfClass:self.class]) {
return (self = cu);
} else {
// expensive operation to unbind cu from c
// but how...?
}
}
if (self=[super init]) {
_c = c;
c->SetUserData((__bridge void *)self);
// expensive operation to bind c to self
// ...
}
return self;
}
@end
這適用於時間之中。現在,我想繼承A
,所以我拿出B
@interface B : A
@property (nonatomic, strong) NSArray *usefulArray;
@end
的問題表面現在爲A
沒有對如何正確地解除綁定實例的知識。所以我要修改上面的代碼到
@interface A : NSObject {
C *_c;
}
-(id)initWithC:(C *)c;
-(void) bind;
-(void) unbind;
@end
@implementation A
-(id)initWithC:(C *)c {
id cu = (__bridge id) c->GetUserData();
if (cu) {
// Bingo, we've got the object already!
if ([cu isKindOfClass:self.class]) {
return (self = cu);
} else {
NSAssert([cu isKindOfClass:[A class]], @"inconsistent wrapper relationship");
[(A *)cu unbind];
}
}
if (self=[super init]) {
_c = c;
c->SetUserData((__bridge void *)self);
[self bind];
}
return self;
}
-(void) bind {
//.. do something about _c
}
-(void) unbind {
// .. do something about _c
_c = nil;
}
@end
現在B
只覆蓋bind
和unbind
,使其工作。
但是當我想到它時,所有B
想要做的就是有一個額外的陣列usefulArray
,它真的保證這麼多工作...?並且編寫unbind
的想法僅適用於您的子類,以與C++對象的1對1關係取代您,這看起來很奇怪(並且效率也很低)。
的確,我在我的代碼中做的是在嘗試使用緩存對象時放置'isKindOfClass:'檢查。不過,我覺得這很不好,並不是很優雅。這就是爲什麼我試圖找到一個完美的解決方案(如果真的存在的話) – lynnard
@ yulan6248:您可以在'B'中覆蓋'wonderfulCache'(只需將該方法複製到B.m),那麼每個子類都會自動擁有自己的緩存。或者,使用「self.class」是關鍵字並且緩存是值的字典。 –
我的真正問題是對ikaver在他的回答中的迴應。我需要在C++對象和它的包裝器之間建立一對一的關係;有一次我意識到我需要繼承這個包裝類。現在我唯一保持一致性的機會是當子類包裝嘗試使用相同的C++對象初始化時,首先撤銷舊包裝對象與其對應的C++對象之間的關係... – lynnard