閱讀609 返回首頁    go 阿裏雲 go 技術社區[雲棲]


Objective-C: delegate的那點事兒

Delegate算是Objective-C的一大特性, 關於Delegate的基礎就不多介紹了, 有興趣的請參看文檔.

這裏僅對Delegate使用中的一些問題做點討論

我們用Delegate很多情況下是基於多線程的,比如我們有一個ViewController在這個Controller裏麵進行了一個下載圖片的操作,下載成功後需要通過protocol來現實下載成功, 但是當ViewController已經被release,而下載工作才結束, 那麼下載工作的[delegate didFinishDownload] (暫且就這麼命名吧) 就會產生一個異常,因為你給一個deallocated的對象發送了一個消息.

那麼,如何解決這個問題呢,首先我們可能想到用if (delegate == nil) 來判斷delegate是否存在,但其實這是不行的,因為已經dealloc的對象並不是nil.要知道Objective-C中給nil發送消息是可以的,所以如果這種方法可行,其實我們就根本不需要if這句,[delegate didFinishDownload] 給nil發送了一個消息也不會出現異常,因此這種方法隻是重複了上麵的錯誤.

還有一個叫[delegate respondsToSelector:SEL]來判斷delegate是否響應一個Selector, 根據上一段的描述,我們也可以判斷出這個也是不行的.這裏額外提一點關於respondsToSelector的東西,要使用這個方法,必須有@protocol MyProtocol <NSObject>,因為respondsToSelector是NSObject的一個protocol方法.

既然要防止delegate被release,那麼retain這個delegate是否可行呢?這麼做雖然避免錯誤的發生,但是也產生了另一個問題,這就關係到Objective-C內存管理中的Retain Circle, 即:有A,B兩個Object, A中有一個B的實例變量,B中又有一個A的實例變量,要release A就必須releaseA中的B,而要release B有必須release B中的A,這樣就產生了一個Retain Circle,A B都不能被dealloc.解決Retain Circle的方法就是使用弱引用(weak reference),弱引用沒有被引用的那個Object的所有權,也就不需要release它,從而解決了Retain Circle問題.為了防止Retain Circle的發生, delegate通常都是弱引用的, 因此我們一般不應該retain一個delegate.但是似乎有一個例外:NSURLConnection, 網上對其的討論結果是:NSURLConnection會retain它的delegate,詳細可以參考StackOverflow上的這個問題

似乎沒有簡單可行的方法來解決這個問題(至少在本文發表時我還沒有找到),那麼我們隻能在通過程序結構的設計來解決這一問題了,對應不同的程序自然也就有不同的解決方法,我想到的一種就是在這個ViewConrtoller被release的時候,把下載方法中的delegate設置成nil即可(目前測試可行, 如有錯誤還請指正).

最後更新:2017-04-04 07:03:12

  上一篇:go 二叉樹的應用-先序遍曆中序遍曆還原二叉樹
  下一篇:go ubuntu屏幕截圖工具:scrot,可截鼠標拖曳的矩形區域圖形