ios delegate你必須知道的事情

當你開始寫iOS程式不久,應該開始面對到不少的delegate,
無論是用別人的library或是本身寫library,可能都逃不了delegate。
為了怕有些人不知道什麼是delegate,在這邊簡單的介紹一下,
delegate中文叫作委託,一般會用在class內部把一些事件處理"委託"給別人去完成。
舉個例子,XML Parser可能他知道怎麼parse xml,可是parse到的東西要怎麼處理xml parser可能不知道。
因此NSXMLParser就提供了一個NSXMLParserDelegate給client去實做,
當parse到某個element的時候,就callback delegate所定義的message,
讓他client本身去決定怎麼去處理這個element。
好吧,我承認我解釋的很模糊,不過我這篇本來就不是要你搞懂什麼是delegate,
而是針對使用或是設計delegate的時候,可能會要注意的事情。

在我們的class中設計delegate的時候,我們一般會有幾個注意事項。
假設個人class叫作MyClass,那我們可能會有定義一個MyClassDelegate這個protocol當做個人delegate protocol。
而MyClass中我們多是這樣寫。
@protocol MyClassDelegate <NSObject>
- (void) myClassOnSomeEvent:(MyClass*)myClass;
@end 

@interface  MyClass
{
    id<MyClassDelegate> _delegate;
}
@property (nonatomic, assign) delegate;
@end
上面的code我們注意到 delegate此property是定義為 @property (assign)
為什麼我們不用retain而要用assign呢?
緣由就是在於iOS的reference counting的環境中,我們必須解決circular count的問題。
讓我們來寫寫我們日常都怎麼用delegate的,下面的code我想你們應該不陌生
- (void)someAction
{
   myClass = [MyClass new];
   myClass.delegate = self;
   ....
}
這邊很快的就出現circular reference了
假設上面的code是寫在一個myViewController的物件當中,
之後一旦myViewController的reference count變成1的時候,
myViewController跟myClass這兩個兄弟兩只剩下互相retain,那就變成了孤島,也就所以形成了memory leak!!!


也因為這樣,iOS官方文件才會要建議我們因此的delegate都要用assign property。
也就是所謂"weak reference"的property,他的特點就是雖然會持有對方的reference,可是不會增長retain count。
如此下來,當myViewController的retain count變成0,則會dealloc。
同時在dealloc中,也一併把myClass release,則myClass也跟著被release。
- (void)dealloc
{
   [myClass release];
   [super dealloc];
}


事情就結束了嗎? 還沒有唷...
這邊還有一個你們經常忘記的重點,那就是上面的dealloc這樣寫會有潛在危險。
應該要改爲這樣
- (void)dealloc
{
   myClass.delegate = nil;    [myClass release];
   [super dealloc];
}
你可能會很納悶,myClass不是馬上就會被release了嗎? 幹嘛要先把他的delegate設成nil?
那是因為我們假設myClass會馬上會被dealloc,可是現實狀況這個是不必定的,
有可能裡面內部有建個NSURLConnection,或是正在作某件事情而讓其餘物件也retain myClass。
若是myClass沒有馬上dealloc,那他的 myClass.delegate不就正指向一個不合法的位置了嗎? (此種pointer稱做dangling pointer)


解決方法是在MyViewController的dealloc中,在release myClass以前,
要先把本來指向本身的delegate改設成nil,這樣才能夠避免crash發生。
在我以前寫的project,很大一部份的crash都是這樣形成的,因為這個問題一般不是每次都發生,
可是發生的時候確很難在從新複製,因此不可不慎啊。


可是很興奮的是到了iOS5中的 Automatic Reference Counting這個問題能夠有所改善。 在ARC中提出了一個新的weak reference的概念來取代本來的assign, weak reference指到的物件如果已經因retain count歸零而dealloc了,則此weak reference也自動設成nil。 而本來舊的這種assign的做法,在ARC中叫作__unsafe_unretained,這只是為了相容iOS4如下的版本。 回顧重點: 若是你是寫library給別人用的,記得把你的delegate設成assign property,這樣纔不會形成circular reference 當你是要始用別人的library,記得在你本身dealloc的時候,把delegate設成nil,以免crash的事情發生。
相關文章
相關標籤/搜索