是 - [NSInvocation retainArguments]复制块?

use*_*008 7 objective-c retain nsinvocation objective-c-blocks

NSInvocation-retainArguments当你不NSInvocation立即运行时,它的方法很有用,但是稍后会这样做; 它保留了对象参数,因此它们在此期间保持有效.

众所周知,应该复制块参数而不是保留.我的问题是,是否-retainArguments知道复制而不是在块类型时保留参数?文档并没有表明它确实如此,但它似乎是一件容易和明智的事情.

更新: iOS 7中的行为似乎已经发生了变化.我刚刚对此进行了测试,并且在iOS 6.1和之前,-retainArguments没有复制块类型的参数.在iOS 7及更高版本中,-retainArguments复制块类型的参数.文档-retainArguments已更新,说它复制块,但它没有说明行为何时发生变化(这对支持旧操作系统的人来说真的很危险).

Bry*_*hen 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];
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

问题是arg在复制块时被修改,这不应该发生,因为这意味着retainArguments调用可能会更改NSInvocation.这将打破许多已经做出的假设。(即从中获取的参数NSInvocation应该与用于创建的参数相同NSInvocation


更新

刚刚做了测试以确认答案是否定的,但我之前的观点是不正确的......

@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
Run Code Online (Sandbox Code Playgroud)

输出,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__
Run Code Online (Sandbox Code Playgroud)

启用 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__
Run Code Online (Sandbox Code Playgroud)

正如你所看到的,c 字符串被复制,retainArguments但不是块。但启用 ARC 后,问题就会消失,因为 ARC 在某个时刻为您复制了它。