Bra*_*son 34 cocoa objective-c automatic-ref-counting
我正在将一段代码迁移到自动引用计数(ARC),并让ARC迁移器抛出错误
NSInvocation的的setArgument是不是安全,可以使用一个对象,具有比__unsafe_unretained其他所有制
在我使用类似的东西分配对象的代码上
NSDecimalNumber *testNumber1 = [[NSDecimalNumber alloc] initWithString:@"1.0"];
Run Code Online (Sandbox Code Playgroud)
然后使用将其设置为NSInvocation参数
[theInvocation setArgument:&testNumber1 atIndex:2];
Run Code Online (Sandbox Code Playgroud)
为什么它阻止你这样做?使用__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;
Run Code Online (Sandbox Code Playgroud)
由于过度发布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;
Run Code Online (Sandbox Code Playgroud)
鉴于编译器不知道该方法是否将通过引用返回(这两个方法声明具有相同的类型和属性),也许迁移器(明智地)谨慎并告诉您避免指向强指针的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
Run Code Online (Sandbox Code Playgroud)
在NSInvocation 默认情况下,不保留或复制给定参数的效率,所以调用调用时,作为参数传递的每个对象都必须仍住.这意味着传递给的指针-setArgument:atIndex:被处理为__unsafe_unretained.
你发布的两行MRR代码就是这样:testNumber1从未发布过.这会导致内存泄漏,但是会有效.但是,在ARC中,testNumber1它将在其最后一次使用和定义它的块的结尾之间的任何地方被释放,因此它将被释放.通过迁移到ARC,代码可能会崩溃,因此ARC迁移工具会阻止您迁移:
NSInvocation的的setArgument是不是安全,可以使用一个对象,具有比__unsafe_unretained其他所有制
简单地将指针作为__unsafe_unretained传递将无法解决问题,您必须确保在调用调用时参数仍然存在.一种方法是-retainArguments像你一样打电话(或者更好:直接在创建之后NSInvocation).然后调用保留其所有参数,因此它保留了被调用所需的所有内容.这可能不是那么有效,但它绝对比崩溃更可取;)
| 归档时间: |
|
| 查看次数: |
8065 次 |
| 最近记录: |