Objective c 在init方法中替换self是一种糟糕的做法吗?

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

我从Appledoc读到这篇关于著名(或臭名昭著?)的
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包装器类和子类的细节。