iOS:阻止属性直接设置访问时崩溃
考虑下面的代码:
@interface ClassA : NSObject @property (nonatomic, copy) void(^blockCopy)(); @end @implementation ClassA @synthesize blockCopy; - (void)giveBlock:(void(^)())inBlock { blockCopy = inBlock; } @end
然后在具有strong
typesClassA
的类中使用它,称为someA
:
self.someA = [[ClassA alloc] init]; [self.someA giveBlock:^{ NSLog(@"self = %@", self); }]; dispatch_async(dispatch_get_main_queue(), ^{ self.someA.blockCopy(); self.someA = nil; });
如果我在启用了ARC的情况下运行了内置的O3
,则在iOS中它会在self.someA.blockCopy();
期间崩溃self.someA.blockCopy();
在objc_retain
里面objc_retain
。 为什么?
现在我意识到人们可能会说我应该用self.blockCopy = inBlock
来设置它,但我确实认为ARC应该在这里做正确的事情。 如果我看一下从giveBlock:
方法生成的程序集(ARMv7),它看起来像这样:
.align 2 .code 16 .thumb_func "-[ClassA giveBlock:]" "-[ClassA giveBlock:]": push {r7, lr} movw r1, :lower16:(_OBJC_IVAR_$_ClassA.blockCopy-(LPC0_0+4)) mov r7, sp movt r1, :upper16:(_OBJC_IVAR_$_ClassA.blockCopy-(LPC0_0+4)) LPC0_0: add r1, pc ldr r1, [r1] add r0, r1 mov r1, r2 blx _objc_storeStrong pop {r7, pc}
这就是调用objc_storeStrong
,它继而在块上retain
,在旧块上release
。 我的猜测是ARC没有正确地注意到它是一个块属性,因为我认为它应该调用objc_retainBlock
而不是正常的objc_retain
。
或者,我只是完全错误,而实际上ARC正在做它所logging的内容,而我刚刚读错了方式?
讨论非常欢迎 – 我觉得这是相当有趣的。
注意事项:
- 它不会在OS X上崩溃。
- 它不会崩溃build立
O0
。
- (void)giveBlock:(void(^)())inBlock { blockCopy = inBlock; }
您需要在分配或传入此function时复制块。 虽然ARC解决了自动移动到堆返回的问题,但它并不是这样做的参数(不能做C的特质)。
它不会在某些环境中崩溃只是巧合; 只要块的堆栈版本没有被覆盖,它就不会崩溃。 一个明确的迹象是,当你有一个崩溃,closures优化消失。 在优化closures的情况下,编译器将不会在任何给定的作用域内重用堆栈内存,导致内存在应用之后长时间“有效”。
我仍然不明白为什么它不能做objc_blockRetain,而不是正常的objc_retain。 编译器知道types毕竟。
我很确定这个问题是任务的潜在成本。 如果块捕获了很多状态,包括潜在的其他块,那么Block_copy()可能真的 非常昂贵。
也就是说,如果你有这样的东西:
BlockType b = ^(...) { ... capture lots of gunk ... }; SomeRandomFunc(b);
…并且仅仅因为这个分配意味着一个Block_copy(),这将使得不可能一致地使用块而没有病态性能问题的风险。 因为编译器没有办法知道SomeRandomFunc()
是同步的还是asynchronous的,所以没有办法自动pipe理这个(在这个时候 – 我确定摆脱这个潜在的tripwire是可取的)。