iOS:阻止属性直接设置访问时崩溃

考虑下面的代码:

@interface ClassA : NSObject @property (nonatomic, copy) void(^blockCopy)(); @end @implementation ClassA @synthesize blockCopy; - (void)giveBlock:(void(^)())inBlock { blockCopy = inBlock; } @end 

然后在具有strongtypesClassA的类中使用它,称为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是可取的)。