2011-05-16 195 views
3

我有一個適用於iPad的pdf閱讀器應用程序,我使用scrollview來顯示每個頁面。我將該頁面保留在視圖中,並且頁面的任一側都可以查看一頁。我有單獨的肖像和風景意見。縱向視圖顯示單個頁面,橫向查看器顯示2個頁面。iOS CATiledLayer崩潰

當iPad改變方向時,我卸載舊方向的視圖並加載新方向的視圖。所以說,它是在縱向視圖,然後更改爲橫向應用程序卸載縱向視圖並加載橫向視圖。除非pdf大,否則這一切都很好。

pdf的使用tiledlayers繪製。當方向改變爲大pdf時,該應用程序正在進行拼圖。如果在瓷磚全部被繪製之前改變方向,則應用程序只會崩潰。我的猜測是,它正在崩潰,因爲它試圖將瓷磚繪製到視圖中,而不是已經卸載。那麼當我卸載視圖時,是否有辦法停止繪製圖塊?

+0

我沒有找到適當的解決方案,但解決方法可以防止崩潰。最後把if(self.tiling == YES && [self.view superview]!= nil)放入 - (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx方法。自動平鋪設置是在視圖從超級視圖中移除之前設置的,或者在您的情況下,在方向更改之前設置。您可以通過對所有方向進行一個視圖來避免此問題。我的解決方法可以防止崩潰,但不會阻止CATiledLayer訪問圖像或以某種神祕的方式鎖定圖像。最終結果是無法從文件系統中刪除所述圖像。祝你好運! – TheBlack 2011-05-16 12:33:53

回答

4

您需要將CALayer的委託設置爲零,然後將其從超級視圖中移除。 這會停止渲染,之後您可以安全地釋放。

- (void)stopTiledRenderingAndRemoveFromSuperlayer; { 
    ((CATiledLayer *)[self layer]).delegate = nil;  
    [self removeFromSuperview]; 
    [self.layer removeFromSuperlayer]; 
} 

此外,請務必從主線程調用此函數,否則可怕的錯誤將等待您。

+0

1)你確定'[self.layer removeFromSuperlayer];'是必需的嗎? 2)您的保留/釋放是否有助於防止崩潰,這一事實並不意味着您的競爭狀況仍然存在?保留防止被調用dealloc(作爲另一個線程釋放的結果),但dealloc仍然可以發生在函數啓動到保留的調用之間,並且最終會在釋放內存中調用retain。 – Danra 2011-11-14 16:53:07

+1

我還發現你必須在設置self.layer.delegate之前刪除任何子視圖否則它們不會被釋放。 – Danra 2011-11-14 16:58:02

+0

你是對的,保留/釋放是不需要的 - 我更新了我的答案。 – steipete 2011-11-15 12:18:29

3

我還沒有看過反彙編看看,但我們正在使用一個稍微不同的解決方案。將CATiledLayer.content屬性設置爲nil塊,並強制所有排隊的渲染塊完成。這可以安全地踢到後臺線程,然後釋放UIView可以被踢回主線程讓視圖和圖層dealloc。

下面是UIViewController dealloc實現的一個示例,它將保持您的擁有視圖的活動時間足夠長,以在不阻止主線程的情況下安全停止呈現。

- (void)dealloc 
{ 
    // This works around a bug where the CATiledLayer background drawing 
    // delegate may still have dispatched blocks awaiting rendering after 
    // the view hierarchy is dead, causing a message to a zombie object. 
    // We'll hold on to tiledView, flush the dispatch queue, 
    // then let go of fastViewer. 
    MyTiledView *tiledView = self.tiledView; 
    if(tiledView) { 
     dispatch_background(^{ 
      // This blocks while CATiledLayer flushes out its queued render blocks. 
      tiledView.layer.contents = nil; 

      dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.01 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 
       // Make sure tiledView survives until now. 
       tiledView.layer.delegate = nil; 
      }); 
     }); 
    } 
} 

這是一個猜測,但有些蘋果的框架/班(StoreKit,CATiledLayer,UIGestureRecognizer)聲稱有@property (weak) id delegate實現,但顯然不正確處理weak委託。看着一些反彙編,他們正在嚴格執行種族限制if != nil檢查,然後直接觸摸弱財產。正確的方法是申報一個__strong Type *delegate = self.delegate,這將成功,並給你一個有力的參考保證生存,或nil,但它肯定不會給你一個殭屍對象的參考(我的猜測是框架代碼尚未升級到ARC)。

在引擎蓋下,CATiledLayer創建了一個調度隊列來完成後臺渲染,並且似乎以不安全的方式觸摸代理屬性,或者獲得本地引用,但沒有使其成爲一個強大的引用。無論哪種方式,如果委託被取消分配,派發的渲染塊將愉快地向殭屍對象發送消息。只是清除代表是不夠的 - 它會減少崩潰的數量,但不能安全地消除它們。

設置content = nil執行dispatch_wait並阻塞,直到所有現有的排隊渲染塊完成。我們反彈回主線程以確保dealloc是安全的。

如果有人有改進建議,請讓我知道。