objective-c - 为什么 ARC 迁移器说 NSInvocation 的 -setArgument : is not safe unless the argument is __unsafe_unretained?

标签 objective-c cocoa automatic-ref-counting

我正在将一段代码迁移到自动引用计数 (ARC),并让 ARC 迁移器抛出错误

NSInvocation's setArgument is not safe to be used with an object with ownership other than __unsafe_unretained

在我使用类似方法分配对象的代码上

NSDecimalNumber *testNumber1 = [[NSDecimalNumber alloc] initWithString:@"1.0"];

然后使用 将其设置为 NSInvocation 参数

[theInvocation setArgument:&testNumber1 atIndex:2];

为什么它会阻止您这样做?使用 __unsafe_unretained 对象作为参数似乎同样糟糕。例如,以下代码在 ARC 下会导致崩溃:

NSDecimalNumber *testNumber1 = [[NSDecimalNumber alloc] initWithString:@"1.0"];
NSMutableArray *testArray = [[NSMutableArray alloc] init];

__unsafe_unretained NSDecimalNumber *tempNumber = testNumber1;

NSLog(@"Array count before invocation: %ld", [testArray count]);
//    [testArray addObject:testNumber1];    
SEL theSelector = @selector(addObject:);
NSMethodSignature *sig = [testArray methodSignatureForSelector:theSelector];
NSInvocation *theInvocation = [NSInvocation invocationWithMethodSignature:sig];
[theInvocation setTarget:testArray];
[theInvocation setSelector:theSelector];
[theInvocation setArgument:&tempNumber atIndex:2];
//        [theInvocation retainArguments];

// Let's say we don't use this invocation until after the original pointer is gone
testNumber1 = nil;

[theInvocation invoke];
theInvocation = nil;

NSLog(@"Array count after invocation: %ld", [testArray count]);
testArray = nil;

由于 testNumber1 的过度释放,因为临时 __unsafe_unretained tempNumber 变量在原始指针设置为nil(模拟在对参数的原始引用消失后使用调用的情况)。如果 -retainArguments 行未注释(导致 NSInvocation 保留参数),则此代码不会崩溃。

如果我将 testNumber1 直接用作 -setArgument: 的参数,则会发生完全相同的崩溃,如果您使用 -retainArguments,它也会得到修复>。那么,为什么 ARC 迁移器说使用强保持指针作为 NSInvocation 的 -setArgument: 的参数是不安全的,除非您使用 __unsafe_unretained 的东西?

最佳答案

这完全是猜测,但这可能与通过引用作为 void* 传递的参数有关吗?

在您提到的情况下,这似乎并不是真正的问题,但如果您要打电话,例如。 getArgument:atIndex: 那么编译器将无法知道是否需要保留返回的参数。

来自 NSInvocation.h:

- (void)getArgument:(void *)argumentLocation atIndex:(NSInteger)idx;
- (void)setArgument:(void *)argumentLocation atIndex:(NSInteger)idx;

鉴于编译器不知道该方法是否会通过引用返回(这两个方法声明具有相同的类型和属性),也许迁移者(明智地)谨慎并告诉您避免指向 strong 的 void 指针指针?

例如:

NSDecimalNumber* val;
[anInvocation getArgument:&val atIndex:2];
anInvocation = nil;
NSLog(@"%@", val); // kaboom!


__unsafe_unretained NSDecimalNumber* tempVal;
[anInvocation getArgument:&tempVal atIndex:2];
NSDecimalNumber* val = tempVal;
anInvocation = nil;
NSLog(@"%@", val); // fine

关于objective-c - 为什么 ARC 迁移器说 NSInvocation 的 -setArgument : is not safe unless the argument is __unsafe_unretained?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8672675/

相关文章:

c++ - 仅移动一个点来改变圆弧的形状

iphone - ARC 和 MRC 之间的区别

ios - 如何调整标签栏角标(Badge)位置?

objective-c - 获取UIAlertView以响应键盘上的返回,并具有与“确定”按钮相同的结果

objective-c - 当标准 NSView 或 NSWindow 被破坏时,是否有可靠的方法来破坏私有(private)数据结构?

objective-c - 如何设置 NSScroll View 的默认位置?

ios - 如何处理 ARC 下的 void* cast?

ios - 如何仅在方法完成后才执行一行代码?

ios - 当我尝试登录 iOS sdk 时无法通过 FBSDKLoginKit 获取访问 token

objective-c - 理解 Objective-C 中的实例化