我从关于著名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