Objective c 在init方法中替换self是一种糟糕的做法吗?
我从Appledoc读到这篇关于著名(或臭名昭著?)的Objective c 在init方法中替换self是一种糟糕的做法吗?,objective-c,subclass,init,Objective C,Subclass,Init,我从Appledoc读到这篇关于著名(或臭名昭著?)的init方法的文章 在某些情况下,init方法可能返回替换对象。因此,在后续代码中,必须始终使用init返回的对象,而不是alloc或allocWithZone:返回的对象 假设我有这两门课 @interface A : NSObject @end @interface B : A @property (nonatomic, strong) NSArray *usefulArray; @end 通过以下实现 @implementation
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
@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
当B调用init
时,[super init]
可能会返回一个被替换的a对象,当B尝试设置属性(a没有)时,它不会导致错误吗
如果这确实导致错误,我们如何以正确的方式实现上述模式
更新:附加一个更真实的特定问题
这是一个C++类,叫做<代码> c>代码>(稍后将使用它)
假设A
的目的是充当C
的包装器;在a
和C
之间始终保持一对一的关系是至关重要的
因此,我提出了以下接口和实现
@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
@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
,它真的保证了这么多的工作吗。。。?编写代码< >绑定的想法只对你的子类替换,在与C++对象的1-1对关系中,你看起来很奇怪(而且效率很低)。
init
方法必须始终返回接收类类型的实例。
如果在-[B init]
中通过-[super init]
调用-[A init]
,self
是一个
(已分配但尚未初始化)类B
的实例。因此-[A init]
必须返回类B
(或子类)的实例
所以,如果您决定“回收”对象,您必须确保
正确的类是循环使用的
我无法判断“在init中替换self”在您的情况下是否是一种不好的做法,那将
可能取决于对象和所使用的条件。这主要是由
“类群集”,如NSNumber
,NSArray
等。您的代码是正确的,不应产生任何错误
他们所说的“可能产生一个替代对象”并不是说它可能返回您所期望的类类型的对象,而是说他们的超级init方法可能会创建同一类的不同实例
因此,[super init]的返回可能是与self
不同的对象,这就是为什么需要执行self=[super init]
而不仅仅是[super init]
。但是,只要没有编码错误,就可以安全地假设对象将按照预期进行初始化
这也是为什么您将self=[super init]
放在if语句中;如果由于某种原因,初始值设定项返回nil
,则您不想继续设置,只想返回self 如果[B init]
从缓存返回A
对象,而不是B
对象,则代码将导致错误。我刚刚用两行简单的代码测试了它A*A=[A new];B*B=[B new]
并且它在-[A setUsefulArray:][/code>中抛出了一个预期的错误无法识别的选择器实际上,我在代码中做的是在尝试使用缓存对象时放入一个isKindOfClass:
检查。然而,我觉得这是黑客和不太优雅。这就是为什么我试图找到一个完美的解决方案(如果真的有)@yulan6248:你可以覆盖B
中的wonderfulCache
(只需将该方法复制到B.m),然后每个子类自动拥有自己的缓存。或者,使用一个字典,其中self.class
是键,缓存是值。我真正的问题是在他的回答中对ikaver的回答。我需要在C++对象与其包装器之间建立一对一的关系;有一次我意识到我需要对这个包装器类进行子类化。现在我唯一的保持一致性的机会是,当一个子类包装器试图用同一个C++对象来初始化时,首先要取消旧包装器对象和它相应的C++对象之间的关系……Yuln6248:我建议你用更多的信息更新你的问题,以便其他读者知道你的“真实问题”。没有浏览所有评论。也许添加一些关于C++类、Objto-C包装器类和子类的细节。