objective-c - 在 init 方法中替换 self 是一种不好的做法吗?

标签 objective-c subclass init

我从 Appledoc 上读到关于著名的(或声名狼藉的?)init 方法

In some cases, an init method might return a substitute object. You must therefore always use the object returned by init, and not the one returned by alloc or allocWithZone:, in subsequent code.

所以说我有这两个类

@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] 可能会返回一个替代的 A 对象,并且当 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 的包装器; AC 始终之间保持一对一的关系是至关重要的。

所以我想出了下面的接口(interface)和实现

@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 不知道如何正确解除绑定(bind)实例。所以只好将上面的代码修改成

@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 只需覆盖 bindunbind 即可使其工作。

但仔细想想,B 想要做的就是拥有一个额外的数组usefulArray,它真的值得做这么多工作吗...?而编写 unbind 只是为了让您的子类在与 C++ 对象的一对一关系中替换您的想法看起来很奇怪(而且效率低下)。

最佳答案

您的代码是正确的,应该不会产生任何错误。

他们所说的“可能会产生一个替代对象”的意思并不是说它可能会返回您期望的类类型的对象,而是他们的 super 初始化方法可能会创建同一类的不同实例。

因此,[super init] 的返回值可能与 self 不同,这就是为什么您需要执行 self = [super init] 而不是只是 [super init]。但是,您可以安全地假设只要您没有编码错误,该对象就会按照您的预期进行初始化。

这也是为什么要将 self = [super init] 放在 if 语句中的原因;如果由于某种原因初始化程序返回 nil,您不想继续设置,只想返回 self。

关于objective-c - 在 init 方法中替换 self 是一种不好的做法吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18374204/

相关文章:

python - 如何使用Python获取子类的完整类名

bash - 如何从/etc/inittab 启动 linux shell

python - 在 Python 类中使用或不使用 init

iphone - 如何在 ios 应用程序中删除某些 ViewControllers 上的状态栏?

objective-c - 在 OS X 应用程序 WebView 中模拟移动 Safari

java - 将子类的实例传递给方法并稍后在该方法中使用它

java - 实例化抽象父类(super class)的子类

ios - 使用表格 View

objective-c - Objective-C 依赖项的弃用警告

swift - 具有属性的子类 UIButton 不会改变 UI