4

init有名な(または悪名高い?)メソッドに関してAppledocからこれを読みました

場合によっては、init メソッドが代替オブジェクトを返すことがあります。したがって、後続のコードでは、alloc または allocWithZone: によって返されるオブジェクトではなく、常に init によって返されるオブジェクトを使用する必要があります。

これら2つのクラスがあるとします

@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のラッパーとして機能することだとします。と の間で常にC1 対 1 の関係を維持することが重要です。AC

だから私は次のインターフェースと実装を思いついた

@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はオーバーライドbindunbindて機能させるだけです。

しかし、私がそれについて考えるとき、Bやりたいことはすべて追加の配列を持つことusefulArrayです.これは本当に多くの作業を保証しますか...? unbindそして、サブクラスが C++ オブジェクトとの 1 対 1 の関係でユーザーを置き換えるためだけに書くという考えは、奇妙に思えます (そして非効率的でもあります)。

4

2 に答える 2