2012-01-11 48 views
9

考虑下面的代码块属性直接设置崩溃iOS版:当访问

self.someA = [[ClassA alloc] init]; 
[self.someA giveBlock:^{ 
    NSLog(@"self = %@", self); 
}]; 
dispatch_async(dispatch_get_main_queue(), ^{ 
    self.someA.blockCopy(); 
    self.someA = nil; 
}); 

如果我运行内置的O3,启用了ARC,在iOS上,它在objc_retain内的self.someA.blockCopy();调用期间崩溃。为什么?

现在我意识到人们可能会说我应该用self.blockCopy = inBlock来设置它,但我确实认为ARC应该在这里做正确的事情。

 .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:如果我看的组件(ARMv7的)从giveBlock:方法,它看起来像这样产生的。我的猜测是ARC没有正确地注意到它是一个块属性,因为我认为它应该调用objc_retainBlock而不是正常的objc_retain

或者,我只是完全错误,实际上ARC正在做它的文件,我刚刚读了它的错误方式?

非常欢迎讨论 - 我觉得这很有趣。

注意要点:

  • 它不会在OS X
  • 它不会崩溃建O0崩溃。

回答

12
- (void)giveBlock:(void(^)())inBlock { 
    blockCopy = inBlock; 
} 

你需要复制块或者在转让或通过成为这项功能。虽然ARC解决了自动移动到堆回收问题,但它并不适用于参数(不能对C的特性)。

它在某些环境中不会崩溃仅仅是巧合;只要堆栈的堆栈版本没有被覆盖,它就不会崩溃。这确实是一个迹象,当你遇到一个崩溃,关闭优化时就会消失。在优化关闭的情况下,编译器不会在任何给定范围内重用堆栈内存,导致内存在应用之后很长一段时间才“有效”。


我还是不太明白,为什么不能做一个objc_blockRetain 而不是普通的objc_retain,虽然。毕竟,编译器知道类型 。

我敢肯定,这个问题是分配的潜在成本。如果该块捕获了很多状态,包括潜在的其他块,那么Block_copy()可能确实是真的确实昂贵。

I.e.如果你有这样的事情:

BlockType b = ^(...) { ... capture lots of gunk ... }; 
SomeRandomFunc(b); 

...这意味着一个Block_copy()仅仅是因为转让的,它将使不可能一直使用块无病理性能问题的风险。因为编译器无法知道SomeRandomFunc()是同步的还是异步的,所以没有办法自动管理它(此时 - 我确定摆脱这种潜在的绊网是可取的)。

+0

虽然在分配时看到“objc_storeStrong”,但我没有能够“做正确的事情”,我只是有点惊讶。 – mattjgalloway 2012-01-11 19:00:14

+0

+1有趣! – Till 2012-01-11 19:02:08

+0

根据我的理解(有一段时间),有些情况会阻止编译器发出在所有情况下都“正常工作”的代码。在ARC之下,沙地中的坚硬线条要求编译器能够准确地证明任何给定的代码模式始终在任何地方都能正常工作。在这种情况下,它不能这样做,因为通过任何给定调用站点(包括'objc_storeStrong()')传递的仅堆栈块参数有效。 – bbum 2012-01-11 20:56:51