protocol和delegate完全不是一回事,放在一起說,只是因爲我們經常在同一個頭文件裏看到這兩個word。
協議(protocol),就是使用了這個協議後就要按照這個協議來辦事,協議要求實現的方法就一定要實現。
委託(delegate),顧名思義就是委託別人辦事,就是當 一件事情發生後,自己不處理,讓別人來處理。
舉個淺顯的例子:
我上班的工作主要內容包括 (1)寫代碼(2)寫文檔(3)測試程序(4)接電話(5)會見客戶
(1)(2)我自己全權負責,但是後面(3)(4)(5)我不想或者不方便自己做,所以我想找個助手(delegate)幫我做這些事,於是我定了一個招聘要求(Protocol),裏寫明我的助手需要會做(3)(4)(5)這三件事。很快,我招到一個助手。
即:我.delegate = 助手;
於是以後每當我遇到需要測試程序或者接電話的活,我就把他轉交給助手(delegate)去處理,助手處理完後如果有處理結果(返回值)助手會告訴我,也許我會拿來用。如果不需要或者沒有結果,我就接着做下面的事。。
protocol和java裏interface的概念類似,是Objective-C語法的一部分。
定義protocol如下
- @protocol ClassADelegate
- - (void)methodA;
- - (void)methodB;
- @end
那麼就是定義了一組函數,這組函數放在一起叫作一個protocol,也就是協議。
函數是需要被實現的,所以如果對於class如下
- @interface ClassB <ClassADelegate> {
- }
- @end
就叫作ClassB conform to protocol ClassADelegate,也就是說ClassB實現了這個協議,
也就是實現了這一組函數。
有了上面這個頭文件,我們就可以放心作調用
- ClassB *b = [[ClassB alloc] init];
- [b methodA];
- [b methodB];
而不用擔心出現unrecognized selector sent to instance這種錯誤了。
所以protocol就是一組函數定義,是從類聲明中剝離出來的一組定義。
- id<ClassADelegate> b = ...;
- [b methodA];
這種用法也常見,b是一個id類型,它知道ClassADelegate這組函數的實現。
那麼delegate是什麼?其實和protocol沒有關係。Delegate本身應該稱爲一種設計模式。
是把一個類自己需要做的一部分事情,讓另一個類(也可以就是自己本身)來完成。
比如ClassC
- @interface ClassC {
- id delegate;
- }
- @end
那麼ClassC的實現(.m文件)裏就可以用delegate這個變量了。
當然這裏完全可以用其它名字而不是delegate。
我們也可以這樣寫
- @interface ClassC {
- ClassB *delegate;
- }
- @end
這樣我們知道了delegate是一個ClassB,它就可以提供ClassB裏的方法。
可以把一部分ClassC裏的工作放在ClassB裏去實現。
這樣的寫法看起來是不是有點奇怪?或者應該寫成這樣?
- @interface ClassC {
- ClassB *classB;
- }
- @end
- …
delegate沒有了…
所以說其實delegate只是一種模式,大家約定俗成,當把自己內部一部分實現暴露給另外一個類去做的時候,就叫實際做事的類爲delegate。
爲什麼會需要把內部實現提出來給另一個類做呢?
最常見的目的就是爲了在隱藏實現的前提下,提供一個自定義的機會。
比如Apple提供的iOS SDK裏就有衆多的delegate,比如最常用的UITableView,
我們沒法知道Apple怎麼重用UITableViewCell,怎麼處理UITableView裏Cell的增加、刪減,因爲我們沒有源碼。
但是我們可以通過實現Delegate的方法來控制一個UITableView的一些行爲。
UITableViewDataSource其實和delegate是一樣一樣的,只是由於意義不同換了個名字罷了。
protocol在此扮演了什麼角色呢?
protocol是一種語法,它提供了一個很方便的、實現delegate模式的機會。
比如寫UITableView的時候,Apple這麼幹
UITableView.m
- - (void)doSomething {
- [self blahblah];
- [self.delegate guruguru];
- [self blahblah];
- }
delegate是我們寫的類,這個類如果可以被傳給UITableView做爲其delegate,那唯一要求,就是它實現了
- (void)guruguru;
這個方法。
如果我們把這個方法定義在一個protocol裏
- @protocol XXXProtocol
- - (void)guruguru;
- @end
就說明了,UITableView需要的delegate是一個conform to XXXProtocol的類。
這就正好是
id<XXXProtocol>
表達的意思。
無論具體的類是什麼,它還有其它什麼方法,只要它conform to這個protocol,
就說明它可以被傳給UITableView,作爲它的delegate。
那麼Apple爲了讓我們知道這個protocol是delegate需要conform的protocol,
它就把XXXProtocol改成了UITableViewDelegate
這樣我們看到protocol的名字裏有Delegate,就知道這個protocol裏的函數是用來做自定義(Customization)的了。
來源:http://haoxiang.org/2011/08/ios-delegate-and-protocol/
Protocol 的其它問題
1. 使用時爲什麼要加上 iOS.delegate = self
物件名稱.delegate = self,是在採用任何協定時 一定會看到的一行程式碼,由於定義協定的類別並不需要實作協定內的方法,因爲實作的部份是由採納協定的類別來實作,但是它又必須要知道是由哪一個類別來實作,因此我們必須要把採納協定類別的 instance 交給定義協定的類別,讓它來使用。
另一方面並不是任何類別都可以將 instance 傳給定義協定的類別來使用,其原因是,我們在定義此協定的類別裏有宣告 delegate 變數時,有限定它必須要採納此協定(id delegate)如果沒有採用該協定就將 instance 傳給定義該協定的類別,Xcode 同樣會發出警告訊息。
2. 爲什麼協定的生效位置不能寫在建構式中
協定的生效位置寫在建構式中,並不會造成程式編譯上的任何問題,因爲這是屬於邏輯上的錯誤,協定要正常生效它必須要知道實作它方法的類別的 instance,如果將生效的位置寫在建構式中,在建立定義此協定的形態的變物件時,它的確會去觸發此協定內的方法,但是由於並沒有給它實作此協定方法類別的 instance,因此不會有任何效果產生,反之,如果一定要將生效的位置寫在建構式中,那麼在初始化時就必須要設定好 delegate 才行,也就是使用初始化的方法函式裏還必須要帶入一個參數物件好指定給 delegate。
3. 在定義協定時同時也可以採用其他的協定
如果在定義協定時同時又採用其他的協定,這會導致之後採納此協定的類別,它必須同時實作出兩個協定內的方法,同樣地,你也可以利用此方式來擴充那些已經存在的協定。
- @protocol FurnaceDelegate <其它可能的協定名稱>
4. 使用 @optional 提供選擇性的實作
@optional,如同它字面上的意義,在 @optional 之後的方法都可以是選擇性的實作,在定義協定時使用此方法,可以讓之後採納此協定的類別不一定要完全實作出協定內的所有方法。
- @protocol FurnaceDelegate
- - (void)whenCalledDelegeteFunction;
- @optional
- -(void)optionalDelegeteFunction;
- @end
來源:http://furnacedigital.blogspot.com/2012/01/protocol.html