3

NSCopying特定のコンテキスト (たとえば、テストのために内部にフックする場合) では、採用しない (実装しない)クラスのインスタンスのコピーを作成できると便利-copyWithZone:です。これはどのように達成できますか?(注: クラスのインスタンス変数のすべてがヘッダーに表示されるわけではないため、カテゴリにプロトコルを実装するだけでは十分ではありません。)

オブジェクトの s を反復処理してみました。Ivar(1) オブジェクト型の再帰 (または保持) の場合、(2) プリミティブ型の場合、元のインスタンス化された ivar のアドレスと作成中のコピーの取得とmemcpyingを試しました。ソースivarのアドレスから宛先ivarのアドレスまでのバッファ。

@interface NSObject (ADDLCopy)

- (id)addl_generateCopyDeep:(BOOL)deepCopy;

@end

@implementation NSObject (ADDLCopy)

//modified from http://stackoverflow.com/a/12265664/1052673
- (void *)addl_ivarPointerForName:(const char *)name
{
    void *res = NULL;

    Ivar ivar = class_getInstanceVariable(self.class, name);

    if (ivar) {
        res = (void *)self + ivar_getOffset(ivar);
    }

    return res;
}


- (id)addl_generateCopyDeep:(BOOL)deepCopy;
{
    id res = [[self.class alloc] init];

    unsigned int count = 0;
    Ivar *ivars = class_copyIvarList(self.class, &count);
    for (unsigned int i = 0; i < count; i++) {
        Ivar ivar = ivars[i];
        //We need to try here because of a bug with NSGetSizeAndAlignment
        //which prevents bitfields from being handled properly and results
        //in an exception being thrown.  See this link for more discussion:
        //http://lists.apple.com/archives/cocoa-dev/2008/Sep/msg00883.html
        @try {
            NSUInteger size = 0;
            const char *encoding = ivar_getTypeEncoding(ivar);
            NSGetSizeAndAlignment(encoding, &size, NULL);
            char firstEncodingCharacter[2];
            strncpy(firstEncodingCharacter, encoding, 1);
            firstEncodingCharacter[1] = 0;
            if (strcmp(firstEncodingCharacter, "@") == 0) {
                if (deepCopy) {
                    id original = object_getIvar(self, ivar);
                    id copy = [original addl_generateCopyDeep:deepCopy];
                    object_setIvar(res, ivar, copy);
                } else {
                    id original = object_getIvar(self, ivar);
                    object_setIvar(res, ivar, original);
                }
            } else {
                const char *name = ivar_getName(ivar);
                void *bytesSource = [self addl_ivarPointerForName:name];
                void *bytesTarget = [res addl_ivarPointerForName:name];
                memcpy(bytesTarget, bytesSource, size);
            }
        }
        @catch (NSException *exception) {}
        @finally {}
    }
    free(ivars);

    return res;
}

@end

これはある程度機能しますが、私が認識していない問題は言うまでもなく、私が認識している明白な問題がいくつかあります。まず第一に、スーパークラスから継承された ivar をコピーしません。第二に、コレクションでは機能しません。また、のバグによりNSGetSizeAndAlignment、ビットマスクでは機能しません。さらに、関連付けられたオブジェクトを考慮できません。

オブジェクトのインスタンス (およびそれが所有するすべてのオブジェクト) を再帰的にコピーするにはどうすればよいですか? これが完全に可能ではない場合 (void ポインターに保持および格納されている所有オブジェクトを処理することは、可能であったとしてもかなり困難に思えます)、どの程度可能でしょうか?

4

1 に答える 1

8

要するに、できません。

「ディープ」コピーは、非常にドメイン固有の問題です。

たとえばdelegate、オブジェクトの をコピーしますか、それともdelegateコピーの を同じオブジェクトに向けたままにしますか (または、それを nil に設定し、コピーするクライアントにそれを設定するように要求しますか)?

この 1 つの例では、コピーに関連するコンテキストごとのビジネス ロジックが無数に存在します (これが、コレクション クラスに「ディープ コピー」の概念が実装されていない理由です)。

上記のすべては、実際に実装の詳細があることを前提としています。フレームワークに実装されたクラスの場合、それらを安全にコピーする方法はありません。なぜなら、メモリのどの部分をコピーする必要があるか、ましてやコピーする必要があるかを知るためのデータさえないからです。

コピー操作の範囲と定義を制限してから、解決策を追求する必要があります。

于 2013-04-05T05:00:35.787 に答える