7

NSInvocationのメソッドは、をすぐに-retainArguments実行せず、後で実行する場合に役立ちます。NSInvocationオブジェクトの引数が保持されるため、この間も有効なままです。

ご存知のように、ブロック引数は保持するのではなく、コピーする必要があります。私の質問は、-retainArgumentsブロック型の場合、引数を保持する代わりにコピーすることを知っていますか? ドキュメントにはそうであるとは書かれていませんが、簡単で賢明なことのように思えます。

更新: iOS 7 で動作が変更されたようです。これをテストしたところ、iOS 6.1 以前で-retainArgumentsは、ブロック タイプのパラメーターはコピーされませんでした。iOS 7 以降で-retainArgumentsは、ブロック型のパラメーターをコピーします。のドキュメントは-retainArguments更新され、ブロックをコピーすると書かれていますが、動作がいつ変更されたかは記載されていません (これは、古い OS をサポートしている人々にとっては非常に危険です)。

4

2 に答える 2

4

それは確かに想定されています(私は自分でテストしていませんが)。ドキュメントによると:

保持引数

レシーバーがまだそうしていない場合は、ターゲットとレシーバーのすべてのオブジェクト引数を保持し、その C 文字列引数とブロックをすべてコピーします。

  • (void)retainArguments

討論

このメソッドが呼び出される前は、argumentsRetained は NO を返します。その後、YES を返します。

効率化のため、新しく作成された NSInvocation オブジェクトは引数を保持またはコピーせず、ターゲットを保持したり、C 文字列をコピーしたり、関連するブロックをコピーしたりしません。NSInvocation オブジェクトをキャッシュする場合は、その引数を保持するように指示する必要があります。そうしないと、呼び出しが呼び出される前に引数が解放される可能性があるためです。NSTimer オブジェクトは、たとえば、タイマーが起動する前に通常遅延があるため、呼び出しに対して常に引数を保持するように指示します。

于 2014-05-22T17:44:29.263 に答える
1

いいえ。

答えが「はい」の場合、NSInvocationブロックをコピーするのに十分スマートな場合、次のようにする必要があります。

for (/*every arguments*/) {
    if (/*arg is object. i.e. @encode(arg) is '@'*/) {
        if ([arg isKindOfClss:[NSBlock class]]) {
            arg = [arg copy]; // copy block
        } else {
            [arg retain];
        }
    }
}

問題はarg、ブロックのコピー中に が変更されることです。これは、呼び出しretainArgumentsが の引数を変更する可能性があるため、発生しないはずNSInvocationです。これは、すでに行われている多くの仮定を破ります。(つまり、から取得するNSInvocation引数は、を作成するために使用される引数と同じである必要がありますNSInvocation)


アップデート

答えを一致させるためにテストを行ったところNOですが、私の前のポイントは間違っていました...

@interface Test : NSObject

@end

@implementation Test

- (void)testMethodWithBlock:(void (^)(void))block obj:(id)obj cstr:(const char *)cstr {
    NSLog(@"%p %p %p %@", block, obj, cstr, [block class]);
}

@end

@implementation testTests

- (void)test1 {
    __block int dummy;
    Test *t = [[Test alloc] init];
    NSMethodSignature *ms = [t methodSignatureForSelector:@selector(testMethodWithBlock:obj:cstr:)];
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:ms];
    void (^block)(void) = ^ {
        dummy++;    // stop this become global block
    };
    id obj = @"object";
    char *cstr = malloc(5);
    strcpy(cstr, "cstr");


    NSLog(@"%@", [ms debugDescription]);

    NSLog(@"%p %p %p %@", block, obj, cstr, [block class]);

    [invocation setSelector:@selector(testMethodWithBlock:obj:cstr:)];
    [invocation setArgument:&block atIndex:2];
    [invocation setArgument:&obj atIndex:3];
    [invocation setArgument:&cstr atIndex:4];

    [invocation invokeWithTarget:t];

    [invocation retainArguments];

    [invocation invokeWithTarget:t];

    free(cstr);
}

@end

出力、ARC 無効 (およびクラッシュ):

2013-04-18 19:49:27.616 test[94555:c07] 0xbfffe120 0x70d2254 0x7167980 __NSStackBlock__
2013-04-18 19:49:27.617 test[94555:c07] 0xbfffe120 0x70d2254 0x7167980 __NSStackBlock__
2013-04-18 19:49:27.618 test[94555:c07] 0xbfffe120 0x70d2254 0x736a810 __NSStackBlock__

ARC 有効:

2013-04-18 19:51:03.979 test[95323:c07] 0x7101e10 0x70d2268 0x7101aa0 __NSMallocBlock__
2013-04-18 19:51:03.979 test[95323:c07] 0x7101e10 0x70d2268 0x7101aa0 __NSMallocBlock__
2013-04-18 19:51:03.980 test[95323:c07] 0x7101e10 0x70d2268 0xe0c1310 __NSMallocBlock__

ご覧のとおり、c 文字列はretainArgumentsブロックではなくコピーされます。しかし、ARC が有効になっていると、ARC がある時点でコピーされたので、問題は解決するはずです。

于 2013-04-18T01:14:25.337 に答える