2

我一直在尋找遊戲中心代碼示例,GKTapper,以及開發人員評論他的實現的一個部分對我沒有太大意義。代碼插入在下面。我不明白的是,爲什麼要調度一個修改主線程視圖控制器的塊不安全?塊和ViewController線程安全

他提到「如果在輔助隊列中執行的塊中引用了viewcontroller,那麼它可能會被釋放到主隊列之外,即使實際塊在主線程上被調度,情況也是如此。如果處理該版本的代碼位於主UI線程上(在主runloop上),那麼這種可能性如何?還是有一些Block/GCD,我沒有得到?

更讓人好奇的是他的解決方案甚至解決了這個問題。 「因爲」callDelegate「是訪問委託的唯一方法,所以我可以確保委託在我的任何塊回調中都不可見。」 (代表是這裏的視圖控制器)

有人能告訴我這件事嗎?我很新的塊和GCD,所以也許我缺少一些簡單的...

// NOTE: GameCenter does not guarantee that callback blocks will be execute on the main thread. 
// As such, your application needs to be very careful in how it handles references to view 
// controllers. If a view controller is referenced in a block that executes on a secondary queue, 
// that view controller may be released (and dealloc'd) outside the main queue. This is true 
// even if the actual block is scheduled on the main thread. In concrete terms, this code 
// snippet is not safe, even though viewController is dispatching to the main queue: 
// 
// [object doSomethingWithCallback: ^() 
// { 
//  dispatch_async(dispatch_get_main_queue(), ^(void) 
//  { 
//   [viewController doSomething]; 
//  }); 
// }]; 
// 
// UIKit view controllers should only be accessed on the main thread, so the snippet above may 
// lead to subtle and hard to trace bugs. Many solutions to this problem exist. In this sample, 
// I'm bottlenecking everything through "callDelegateOnMainThread" which calls "callDelegate". 
// Because "callDelegate" is the only method to access the delegate, I can ensure that delegate 
// is not visible in any of my block callbacks. 
// *** Delegate in this case is a view controller. *** 
- (void) callDelegate: (SEL) selector withArg: (id) arg error: (NSError*) err 
{ 
    assert([NSThread isMainThread]); 
    if([delegate respondsToSelector: selector]) 
    { 
     if(arg != NULL) 
     { 
      [delegate performSelector: selector withObject: arg withObject: err]; 
     } 
     else 
     { 
      [delegate performSelector: selector withObject: err]; 
     } 
    } 
    else 
    { 
     NSLog(@"Missed Method"); 
    } 
} 

- (void) callDelegateOnMainThread: (SEL) selector withArg: (id) arg error: (NSError*) err 
{ 
    dispatch_async(dispatch_get_main_queue(), ^(void) 
    { 
     [self callDelegate: selector withArg: arg error: err]; 
    }); 
} 

- (void) authenticateLocalUser 
{ 
    if([GKLocalPlayer localPlayer].authenticated == NO) 
    { 
     [[GKLocalPlayer localPlayer] authenticateWithCompletionHandler:^(NSError *error) 
     { 
      [self callDelegateOnMainThread: @selector(processGameCenterAuth:) withArg: NULL error: error]; 
     }]; 
    } 
} 

回答

5

問題是,塊有自己的狀態。如果使用變量創建塊,則該變量可能會被複制並在塊的生命週期中保持不變。因此,如果您創建一個使用指向視圖控制器的指針的塊,那麼該指針可能會被複制,如果指針引用的內容隨後被解除分配,那麼這是一件壞事。

考慮以下順序:

  1. 創建引用您的視圖控制器塊,並把它送給遊戲中心。
  2. 各種東西發生在主線程上,導致視圖控制器被釋放和釋放。
  3. 遊戲中心執行你給它的區塊。它仍然有一個指向視圖控制器的指針,但視圖控制器不再存在。
  4. 崩潰。

您顯示的代碼通過確保該塊不包含指向處於其狀態的視圖控制器的指針來避免此問題。它只是在主線程上調用一個方法,並且該方法使用它自己的最新指針來訪問視圖控制器。如果視圖控制器被釋放,這個指針應該被設置爲零,所以沒有什麼不好的事情發生。

+0

現在有道理啊,謝謝! – SunnyPianist 2011-04-03 10:16:34

+0

事實上,我做了一些更多的研究之後,我有點困惑。根據我讀的內容,塊中的對象將被保留,例如在塊中引用的變量,「self」將被保留(因爲實例變量=自 - > ivar)。 在這種情況下,由於「viewcontroller」(或「委託」)是一個實例變量,「self」應該被塊保留嗎?如果這是真的,那麼塊不會獲得viewcontroller/delegate的最新值?指向該對象的「自我」指針仍然有效,自我委託應該仍然可以。 – SunnyPianist 2011-04-04 21:04:20

1

我對塊和GCD也很新穎,這就是爲什麼我在Google中發現你的問題。

但是,在閱讀另一篇討論後,我想也許迦勒的答案不完全正確? Block_release deallocating UI objects on a background thread

在其他討論中,拉爾夫說: 「UIKit的對象不一樣的主線程以外的地方去分配」

而且從GKTapper評論: 「視圖控制器可能被釋放(和dealloc'd)主隊列」

外我想情況更像:

  1. 你創建的視圖控制器(保留計數= 1)
  2. 你創建的塊也保持此視圖控制器(保留計數= 2)在(1)被釋放創建第一
  3. 視圖控制器(保留計數= 1)
  4. 塊被在一個輔助線程執行時,然後釋放(保留計數= 0)
  5. 視圖控制器被dealloc'd主線程之外

不知道這是正確的,但在目前,這是我的理解。