在iOS
開發(fā)中表蝙,Protocol
是一種經(jīng)常用到的設(shè)計模式瘪弓,蘋果的系統(tǒng)框架中也普遍用到了這種方式宠哄,比如UITableView
中的<UITableViewDelegate>
壹将,以及<NSCopying>
、<NSObject>
這樣的協(xié)議毛嫉。我想大家也都自定義過協(xié)議诽俯,一般都用于回調(diào),或者數(shù)據(jù)傳遞承粤。不過暴区,用久了以后,不知道大家是否會有一些困惑:協(xié)議只能用于委托代理嗎辛臊?為什么系統(tǒng)中定義了這么多協(xié)議仙粱?為什么感覺分類和協(xié)議好像?系統(tǒng)中又為何會定義這么多分類彻舰?被這些問題轟炸后伐割,我不禁又開始想候味,協(xié)議到底是個啥玩意?
帶著這樣的目地隔心,我閱讀了一些文檔書籍白群,最后整理了以下的內(nèi)容:
-
protocol
是什么? -
protocol
里有些啥? -
protocol
可以寫在哪? -
protocol
里的方法由誰實現(xiàn),由誰調(diào)用? -
protocol
分類:正式協(xié)議和非正式協(xié)議硬霍? -
protocol
有哪些作用帜慢,能用在哪些地方?
1. protocol 是什么?
iOS 開發(fā)文檔是這樣定義的:(翻譯略渣,請見諒)
A protocol declares a programmatic interface that any class may choose to implement. Protocols make it possible for two classes distantly related by inheritance to communicate with each other to accomplish a certain goal. They thus offer an alternative to subclassing. Any class that can provide behavior useful to other classes may declare a programmatic interface for vending that behavior anonymously. Any other class may choose to adopt the protocol and implement one or more of its methods, thereby making use of the behavior. The class that declares a protocol is expected to call the methods in the protocol if they are implemented by the protocol adopter.
協(xié)議聲明了任何類都能夠選擇實現(xiàn)的程序接口唯卖。協(xié)議能夠使兩個不同繼承樹上的類相互交流并完成特定的目的崖堤,因此它提供了除繼承外的另一種選擇。任何能夠為其他類提供有用行為的類都能夠聲明接口來匿名的傳達這個行為耐床。任何其他類都能夠選擇遵守這個協(xié)議并實現(xiàn)其中的一個或多個方法密幔,從而利用這個行為。如果協(xié)議遵守者實現(xiàn)了協(xié)議中的方法撩轰,那么聲明協(xié)議的類就能夠通過遵守者調(diào)用協(xié)議中的方法胯甩。
2. protocol 里有些啥?
協(xié)議中能夠聲明方法,以及屬性堪嫂。然后問題就來了偎箫,不是不能定義成員變量的嗎?
對皆串,的確不能定義成員變量淹办,但是屬性是什么?屬性包含了三個東西:成員變量恶复、setter
方法怜森、getter
方法。在類中定義的屬性谤牡,當(dāng)然三者都有副硅,然而協(xié)議中定義的屬性只有獲取和設(shè)置方法,沒有成員變量翅萤,這就要求該協(xié)議的遵守者必須自己寫出setter
和getter
方法的實現(xiàn)恐疲。但是有一種情況是不需要的,那就是遵守者本來就有這個屬性套么,此時系統(tǒng)會為這個屬性自動生成設(shè)置獲取方法培己,既然已經(jīng)實現(xiàn)了,那么遵守者就沒必要去實現(xiàn)協(xié)議中的這個屬性了胚泌。
盡管可以實現(xiàn)“偽屬性”省咨,但是,我們還是應(yīng)該盡量把屬性定義在主接口中诸迟,而不應(yīng)該定義在協(xié)議中茸炒。
還有一點,也是很重要的一點阵苇,為什么自定義的協(xié)議后面會有這么一個東西<NSObject>
?
協(xié)議也能繼承壁公。既可以繼承自自定義的協(xié)議,也可以繼承自系統(tǒng)的協(xié)議绅项。
我們在定義協(xié)議的時候紊册,一般都是直接繼承自<NSObject>
,為什么系統(tǒng)要默認讓協(xié)議繼承自這個協(xié)議呢快耿?
因為這個協(xié)議中定義了一些基本的方法囊陡,由于我們使用的所有類都繼承NSObject
這個基類,而這個基類遵守了<NSObject>
這個協(xié)議掀亥,那么也就實現(xiàn)了其中的那些方法撞反,這些方法當(dāng)然可以由NSObject
及其子類對象調(diào)用,但是在不知道遵守者類型的時候需要用到id <協(xié)議名>
這樣的指針搪花,這個指針在編譯期并不知道自己指向哪個對象遏片,唯一能調(diào)用的便是協(xié)議中的方法,然而有時候又需要用一些基本的方法撮竿,比如要辨別id <協(xié)議名>
這個指針?biāo)傅膶ο髮儆谀膫€類吮便,就要用到-isMemberOf:
這個方法,而這個方法是<NSObject>
這個協(xié)議中的方法之一幢踏,所以髓需,我們自定義的協(xié)議都需要繼承<NSObject>
。本段一開始便說道:<NSObject>
中的方法在NSObject
基類中實現(xiàn)了房蝉,那么無需再關(guān)心實現(xiàn)了僚匆,直接調(diào)用<NSObject>
中的方法吧。
3. protocol 可以寫在哪搭幻?
寫在頭文件中白热,寫在實現(xiàn)文件的類擴展中。
前者:可以當(dāng)做是給這個類添加了一些外部接口粗卜。
后者:可以當(dāng)做是給這個類添加了一些私有接口屋确。
- 寫在頭文件中,類內(nèi)部自然能通過
self
調(diào)用续扔,外部也可以調(diào)用里面的方法攻臀,子類可以實現(xiàn)或者重寫里面的方法。 - 而在類擴展中纱昧,內(nèi)部可以調(diào)用刨啸,外部不能調(diào)用、子類不能重寫實現(xiàn)和重寫识脆,相當(dāng)于是私有方法设联。
不過善已,如果子類自身又遵循了這個協(xié)議,但并沒有實現(xiàn)离例,那么在運行時换团,系統(tǒng)會一級級往上查找,直到找到父類的方法實現(xiàn)宫蛆。也就是說艘包,只要知道蘋果的私有方法名,并且確保自己的類是這個私有方法所屬類的子類耀盗,就可以在子類中通過只聲明不實現(xiàn)的方式執(zhí)行父類中該私有方法的實現(xiàn)想虎。
4. protocol 里的方法由誰實現(xiàn),由誰調(diào)用
實現(xiàn):遵守協(xié)議者及其子類
調(diào)用:遵守協(xié)議者叛拷、其子類舌厨、id <協(xié)議名>
5. protocol 的分類:正式協(xié)議 和 非正式協(xié)議(類別)
iOS 文檔是這樣定義的:
There are two varieties of protocol, formal and informal:
A formal protocol declares a list of methods that client classes are expected to implement. Formal protocols have their own declaration, adoption, and type-checking syntax. You can designate methods whose implementation is required or optional with the @required and @optional keywords. Subclasses inherit formal protocols adopted by their ancestors. A formal protocol can also adopt other protocols. Formal protocols are an extension to the Objective-C language.
An informal protocol is a category on NSObject, which implicitly makes almost all objects adopters of the protocol. (A category is a language feature that enables you to add methods to a class without subclassing it.) Implementation of the methods in an informal protocol is optional. Before invoking a method, the calling object checks to see whether the target object implements it. Until optional protocol methods were introduced in Objective-C 2.0, informal protocols were essential to the way Foundation and AppKit classes implemented delegation.
正式協(xié)議聲明了一系列需要遵守協(xié)議者實現(xiàn)的方法。正式協(xié)議擁有它們特有的聲明忿薇,遵守邓线,類型判斷的語法。你可以通過
@required
和@optional
關(guān)鍵詞來指定哪些方法是必須實現(xiàn)的以及哪些方法選擇實現(xiàn)煌恢。子類繼承父類遵守的協(xié)議骇陈,正式協(xié)議也可以遵守其他協(xié)議。非正式協(xié)議是基于
NSObject
的類目瑰抵,其所有子類都含蓄地遵守了這個協(xié)議(類目是一種語言特性你雌,它能夠不用繼承便為類添加方法)在非正式協(xié)議中的方法是可以選擇實現(xiàn)的。在調(diào)用一個方法之前二汛,調(diào)用者要確認目標(biāo)對象是否實現(xiàn)了方法婿崭。在OC 2.0
引入可選正式協(xié)議方法之前,非正式協(xié)議是Foundation
和AppKit
框架中的類中實現(xiàn)委托的唯一方式肴颊。
類目實際上是一種特殊的協(xié)議氓栈。我們沒法通過正式協(xié)議為系統(tǒng)的類添加方法,因為我們無法編輯系統(tǒng)的類婿着。當(dāng)然授瘦,我們也可以選擇繼承的方式,但是竟宋,這就會創(chuàng)建一個新的類提完,并不是特別劃算。所以丘侠,類目這種方式派上用場了徒欣。
但是為什么系統(tǒng)框架中使用了這么多的類目呢?設(shè)計者當(dāng)初為什么不把類目中的方法寫到主接口中蜗字?
原因在于要將眾多的方法打散到各處打肝,要將同一類型功能的接口都封裝在一個類目中脂新。這樣做能夠減少主接口中的代碼量,也便于調(diào)試粗梭。我們還可以把類中的私有方法全部封裝在名為Private
的類目中争便,然后在實現(xiàn)文件中引入這個類目,這樣做可以把共有的方法定義在一個類目中楼吃,也很容易能夠清楚哪些是私有方法。
協(xié)議妄讯,尤其是類目孩锡,一定要給類目的名稱和方法名添加自己的特有前綴,這樣做既不會和系統(tǒng)的類目沖突亥贸,也不會在將自己的代碼開源后和其他使用者的類目沖突躬窜。
6. protocol 有哪些作用,用在哪些地方
某一個類需要委托其他類處理某些事件炕置,最具代表性性的便是
UITableView
的那些代理方法荣挨。這些方法其實還是代理的方法,只不過定義的地方可能會在委托者類中朴摊,通過調(diào)用這些方法默垄,可以:將委托者中的數(shù)據(jù)傳遞給代理;將代理的數(shù)據(jù)傳遞給委托者甚纲;將委托者的事件拋給代理去處理...給某幾個特定的類添加統(tǒng)一的接口口锭,這些接口是從這些類中抽象出的共同的行為,這樣便可以減少重復(fù)的代碼介杆。
總結(jié)
協(xié)議就是定義公共接口的地方鹃操,只要遵守協(xié)議,就等于在頭文件中定義了這些方法春哨,只要實現(xiàn)就行了荆隘。之所以有這樣的設(shè)計,是因為要將共同的行為抽象出來:不同的類有不同的作用和特征赴背,這也是面向?qū)ο蟮奶攸c椰拒,但是即使千差萬別,還是會有某些相似點的凰荚,這些相似的地方就可以抽象出來做成協(xié)議耸三。但有時候這些共同的部分并不是本身就有的,而是人為的添加的浇揩,我們要求這些類具有共同的部分仪壮,而不管這些類是多么千差萬別。有人會問胳徽,為什么不寫一個公共的父類呢积锅?子類繼承父類爽彤,這樣就能共有某些方法了?
當(dāng)然是有這樣的設(shè)計的缚陷,Foundation
框架下類的設(shè)計就是這樣一層一層寫下去的适篙,最具相同性的屬性和方法聲明的位置絕對更靠前。但是箫爷,如果同時需要遵守協(xié)議的是來自兩個不同繼承樹上的類呢嚷节?難道是找到它們共有的祖先類,然后把方法寫在那里面嗎虎锚?顯然這么做是不行的硫痰,因為這樣會導(dǎo)致兩個繼承樹下的所有子類都可以調(diào)用,然而并不是所有子類都需要這些方法窜护,所以還是得要用協(xié)議效斑。
很多人會看到有一個<NSObject>
的協(xié)議,這個協(xié)議和NSObject
這個類同名柱徙,由NSObject
遵守缓屠,為什么不把這些方法直接寫到NSObject
類中呢?因為cocoa
框架中的基類不止NSObject
一個护侮,還有NSProxy
這樣的類存在敌完,那么<NSObject>
這個協(xié)議就很容易明白了,它抽象出了所有基類都需要的方法羊初,為基類提供共有方法蠢挡。還有一個原因的話,在上文中已經(jīng)說明了凳忙,自定義的類要繼承自這個協(xié)議业踏,以供匿名對象id<協(xié)議名>
使用。