2012-01-11 64 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