Objective-C——消息、Category和Protocol

轉自:http://www.cnblogs.com/chijianqiang/archive/2012/06/22/objc-category-protocol.htmlhtml

 

面向對象永遠是個能夠吐槽的話題,從開始提出到推崇備至,到充滿質疑,一路走來讓人唏噓不已。面向對象的思想可謂歷史悠久,20世紀70年代的Smalltalk能夠說是面嚮對象語言的經典,直到今天咱們依然將這門語言視爲面嚮對象語言的基礎。程序員

面向對象是大部分編程語言的基本特性,像C++、Java、Objective-C這樣的靜態語言,Ruby、Python這樣的動態語言都是面向對象的語言。可是如何編寫面向對象的程序卻一直是困擾人們的話題,即便是Smalltalk,也有人認爲這是一個有缺陷的面向對象的語言實現。
 
我在2010年翻譯過的一篇InfoQ的文章,《面向對象編程──走錯了路》中提到,面向對象編程的三個原則是:基於消息傳遞機制,對象分離和多態。文章中還舉了Erlang例子,認爲Erlang具有了這些原則,「因此多是惟一的面嚮對象語言」。除了以前提到的三個特徵,單繼承和動態類型也被引用爲面嚮對象語言的「絕對需求」。基於這些考慮,文章指出,Smalltalk在實現對象思想時的「錯誤」──例如,只關注狀態和行爲,在類和基於映像的語言裏缺少良好的併發模型和消息機制。
 
這篇文章中的核心就是,面向對象思想中除了對象的狀態、行爲,還應該關注其併發機制、消息機制,後者更爲重要。這一點事實上是我在接觸了Objective-C以後纔有了更深刻的體會。
 
Ojbective-C的語法設計主要基於Smalltalk,除了提供傳統的面向對象編程特性以外,還增長了不少相似動態語言Ruby、Python才具備的特性,例如動態類型、動態加載、動態綁定等等,同時強化了消息傳遞機制和表意(Intention Revealing Interface)接口的概念。
 
—消息—
消息傳遞模型(Message Passing)是Objective-C語言的核心機制。在Objective-C中,沒有方法調用這種說法,只有消息傳遞。在C++或Java中調用某個類的方法,在Objective-C中是給該類發送一個消息。在C++或Java裏,類與類的行爲方法之間的關係很是緊密,一個方法一定屬於一個類,且於編譯時就已經綁定在一塊兒,因此你不可能調用一個類裏沒有的方法。而在Objective-C中就比較簡單了,類和消息之間是鬆耦合的,方法調用只是向某個類發送一個消息,該類能夠在運行時再肯定怎麼處理接受到的消息。也就是說,一個類不保證必定會響應接收到的消息,若是收到了一個沒法處理的消息,那麼程序就是簡單報一個錯。甚至你能夠向一個值爲nil的空對象發送消息,系統都不會出錯或宕掉。這種設計自己也比較符合軟件的隱喻。
 
在表意接口(Intention Revealing Interface)方面,Objective-C也是設計的比較出色的語言。面嚮對象語言的特性之一就是經過API把實現封裝起來,爲上層建築提供服務。可是須要注意的一點就是,你封裝的API最好可以讓調用者看到接口描述就知道怎麼使用。若是爲了使用一個API必需要去研究它的實現,那麼就失去了封裝的意義。Objective-C經過顯式的API描述,讓開發者不自覺的寫出知足表意接口的API,好比下圖中的API描述。

 
上圖中描述了一個傳統意義的實例方法,但和Java或C++不一樣的是,其方法關鍵字由多個字符串組成,在這個例子是insertObject和atIndex,(id)anObject和(NSUInterger)index分別表示參數類型和參數名稱。整個方法看上去就像一個英語句子,咱們能夠很容易的知道,這個方法就是在索引爲index處插入一個對象。若是你是從其餘語言轉到Objective-C,那麼開始的時候會感受這種寫法有些繁複,可是一旦理解並習慣了你會感覺到其巨大的好處,這種寫法會強制你寫出優美易讀的代碼和API,並且有了XCode強大的提示功能,再長的方法也是一蹴而就。
 
下面咱們來講說多態和繼承。
 與Java同樣,Objective-C同樣不支持多重繼承,可是經過類別(Category)和協議(Protocol)能夠很好的實現代碼複用和擴展。
 
—Category—
首先咱們來談談Category。
 
Objective-C提供了一種不同凡響的方式——Category,能夠動態的爲已經存在的類添加新的行爲。這樣能夠保證類的原始設計規模較小,功能增長時再逐步擴展。使用Category對類進行擴展時,不須要訪問其源代碼,也不須要建立子類。Category使用簡單的方式,實現了類的相關方法的模塊化,把不一樣的類方法分配到不一樣的分類文件中。
 
實現起來很簡單,咱們舉例說明。
SomeClass.h @interface SomeClass : NSObject{ } -(void) print; @end 
 
這是類SomeClass的聲明文件,其中包含一個實例方法print。若是咱們想在不修改原始類、不增長子類的狀況下,爲該類增長一個hello的方法,只須要簡單的定義兩個文件SomeClass+Hello.h和SomeClass+Hello.m,在聲明文件和實現文件中用「()」把Category的名稱括起來便可。聲明文件代碼以下:
 
#import "SomeClass.h"
 
@interface SomeClass (Hello)
-(void)hello;
@end
實現文件代碼以下
#import "SomeClass+Hello.h" @implementationSomeClass (Hello) -(void)hello{ NSLog (@"name:%@ ", @"Jacky"); } @end 
其中Hello是Category的名稱,若是你用XCode建立Category,那麼須要填寫的內容包括名稱和要擴展的類的名稱。這裏還有一個約定成俗的習慣,將聲明文件和實現文件名稱統一採用「原類名+Category」的方式命名。
調用也很是簡單,毫無壓力,以下:
首先引入Category的聲明文件,而後正常調用便可。
#import "SomeClass+Hello.h" SomeClass * sc =[[SomeClass alloc] init]; [sc hello] 
執行結果是:
nameJacky 
 
Category的使用場景:
一、當你在定義類的時候,在某些狀況下(例如需求變動),你可能想要爲其中的某個或幾個類中添加方法。
二、一個類中包含了許多不一樣的方法須要實現,而這些方法須要不一樣團隊的成員實現
三、當你在使用基礎類庫中的類時,你可能但願這些類實現一些你須要的方法。
 
遇到以上這些需求,Category能夠幫助你解決問題。固然,使用Category也有些問題須要注意,
一、Category能夠訪問原始類的實例變量,但不能添加變量,若是想添加變量,能夠考慮經過繼承建立子類。
二、Category能夠重載原始類的方法,但不推薦這麼作,這麼作的後果是你不再能訪問原來的方法。若是確實要重載,正確的選擇是建立子類。
三、和普通接口有所區別的是,在分類的實現文件中能夠沒必要實現全部聲明的方法,只要你不去調用它。
 
用好Category能夠充分利用Objective-C的動態特性,編寫出靈活簡潔的代碼。
 
—Protocol— 
下面咱們再來看Protocol。
Protocol,簡單來講就是一系列不屬於任何類的方法列表,其中聲明的方法能夠被任何類實現。這種模式通常稱爲代理(delegation)模式。你經過Protocol定義各類行爲,在不一樣的場景採用不一樣的實現方式。在iOS和OS X開發中,Apple採用了大量的代理模式來實現MVC中View和Controller的解耦。
 
定義Protocol很簡單,在聲明文件(h文件)中經過關鍵字@protocol定義,而後給出Protocol的名稱,方法列表,而後用@end表示Protocol結束。在@end指令結束以前定義的方法,都屬於這個Protocol。例如:
複製代碼
@protocol ProcessDataDelegate <NSObject> @required - (void) processSuccessful: (BOOL)success; @optional - (id) submitOrder: (NSNumber *) orderid; @end
複製代碼
 
以上代碼能夠單獨放在一個h文件中,也能夠寫在相關類的h文件中,能夠視具體狀況而定。該Protocol包含兩個方法,processSuccessful和submitOrder。這裏還有兩個關鍵字,@required和@optional,表示若是要實現這個協議,那麼processSuccessful方法是必需要實現的,submitOrder則是可選的,這兩個註解關鍵字是在Objective-C 2.0以後加入的語法特性。若是不註明,那麼方法默認是@required的,必須實現。
 
那麼如何實現這個Protocol呢,很簡單,建立一個普通的Objective-C類,取名爲TestAppDelegate,這時會生成一個h文件和m文件。在h文件中引入包含Protocol的h文件,以後聲明採用這個Protocol便可,以下:
@interface TestAppDelegate : NSObject<ProcessDataDelegate>; @end
用尖括號(<...>)括起來的ProcessDataDelegate就是咱們建立的Protocol。若是要採用多個Protocol,能夠在尖括號內引入多個Protocol名稱,並用逗號隔開便可。例如<ProcessDataDelegate,xxxDelegate>
 
m文件以下:
複製代碼
@implementation TestAppDelegate - (void) processSuccessful: (BOOL)success{ if (success) { NSLog(@"成功"); }else { NSLog(@"失敗"); } } @end 
複製代碼
因爲submitOrder方法是可選的,因此咱們能夠只實現processSuccessful。
 
Protocol通常使用在哪些場景呢?Objective-C裏的Protocol和Java語言中的接口很相似,若是一些類之間沒有繼承關係,可是又具有某些相同的行爲,則可使用Protocol來描述它們的關係。不一樣的類,能夠遵照同一個Protocol,在不一樣的場景下注入不一樣的實例,實現不一樣的功能。其中最經常使用的就是委託代理模式,Cocoa框架中大量採用了這種模式實現數據和UI的分離。例如UIView產生的全部事件,都是經過委託的方式交給Controller完成。根據約定,框架中後綴爲Delegate的都是Protocol,例如UIApplicationDelegate,UIWebViewDelegate等,使用時你們能夠留意一下,體會其用法。
 
使用Protocol時還須要注意的是:
一、Protocol自己是能夠繼承的,好比:
@protocol A -(void)methodA; @end
@protocol B <A>
     -(void)methodB; @end

若是你要實現B,那麼methodA和methodB都須要實現。 編程

二、Protocol是類無關的,任何類均可以實現定義好的Protocol。若是咱們想知道某個類是否實現了某個Protocol,還可使用conformsToProtocol進行判斷,以下:
[obj conformsToProtocol:@protocol(ProcessDataDelegate)] 
 
好吧,具體的語言特性此次就介紹這麼多。從某種意義上來講,Objective-C是一門古老的語言,發明於1980年。1988年,喬布斯的Next公司得到了Objective-C語言的受權,並開發出了Objective-C的語言庫和NEXTSTEP的開發環境。NextStep是以Mach和BSD爲基礎,Objective-C是其語言和運行庫,後來的事你們都清楚,蘋果買了Next,喬布斯迴歸蘋果,開始神奇的蘋果振興之路,NextStep成了Max OS X的基礎。之後發展愈來愈好,Objctive-C成了Apple的當家語言,如今基本上是Apple在維護Objctive-C的發展。
 
在蘋果的AppStore推出以前,Objective-C一直相對小衆,可是其優秀的語言特性彷佛一直在爲後面的爆發積蓄力量,當蘋果平臺級的應用出現以後,Objective-C開始大放異彩,靜態語言的效率和動態語言的特性獲得衆多程序員的喜好,目前它已經以火箭般的速度躥升TIOBE語言排行版第四位。
 
對於喜好蘋果技術的技術人員來講,Objective-C是你必須深刻了解和值得學習的一門語言,但願之後有機會多寫一些相關的文章。
相關文章
相關標籤/搜索