是 - [NSInvocation retainArguments]复制块?

时间:2013-04-18 00:52:29

标签: objective-c objective-c-blocks retain nsinvocation

NSInvocation的{​​{1}}方法对于您不立即运行-retainArguments但后来执行此操作非常有用;它保留了对象参数,因此它们在此期间保持有效。

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

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

2 个答案:

答案 0 :(得分:4)

当然应该(虽然我自己没有测试过)。根据{{​​3}}:

  

retainArguments

     

如果接收方尚未这样做,请保留   目标和接收器的所有对象参数并复制它的所有参数   C字符串参数和块。

     
      
  • (void)retainArguments
  •   
     

讨论

     

在调用此方法之前,argumentsRetained返回NO;之后,它返回YES。

     

为了提高效率,新创建的NSInvocation对象不会保留或   复制他们的论点,也不保留他们的目标,复制C.   字符串,或复制任何相关的块。你应该指导一个   NSInvocation对象如果要缓存它,则保留其参数,   因为参数可能会在调用之前释放   被调用。 NSTimer对象总是指示它们的调用   例如,保留他们的论点,因为通常会有延迟   在计时器开火之前。

答案 1 :(得分: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的参数相同


<强>更新

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

@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会在某些时候为你复制它。