Objective-C之成魔之路【12-分類與協議】,objective-c12-
郝萌主傾心貢獻,尊重作者的勞動成果,請勿轉載。
如果文章對您有所協助,歡迎給作者捐贈,支援郝萌主,捐贈數額隨意,重在心意^_^
我要捐贈: 點擊捐贈
Cocos2d-X源碼下載:點我傳送
分類與協議是OC比較有特色的部分。從表面來看,分類呢有點類似抽象方法在抽象類別中(C++或者Java裡的那個抽象類別概念)。協議類似介面(Java語言那個介面),但是又不能“一視同仁”。
分類概念分類(Category) 允許向一個類檔案中添加新的方法聲明, 它不需要使用子類機制, 並且在類實現的檔案中的同一個名字下定義這些方法。 其文法舉例如下: #import "ClassName.h"
@interface ClassName ( CategoryName )
// 方法聲明
@end分類執行個體
上一篇多態性介紹中曾經使用過Vector和Scalar的例子, 下面我們為Vector增加“減”sub的方法。Vector+sub.h檔案
#import <Foundation/Foundation.h>#import "Vector.h"@interface Vector (sub) -(Vector *) sub: (Vector *) v; @end
Vector+sub.m檔案
#import "Vector+sub.h"@implementation Vector (sub) -(Vector *) sub: (Vector *) v {Vector *result = [[Vector alloc] init];[result setVec1: vec1 - [v vec1] andVec2: vec2 - [v vec2]]; return result;}@end調用的main函數
#import <Foundation/Foundation.h>#import "Vector+sub.h"int main (int argc, const char * argv[]) {Vector *vecA =[[Vector alloc] init];Vector *vecB =[[Vector alloc] init];id result;//set the values[vecA setVec1: 3.2 andVec2: 4.7];[vecB setVec1: 32.2 andVec2: 47.7];// print it[vecA print];NSLog(@" + ");[vecB print];NSLog(@" = ");result = [vecA add: vecB];[result print];[vecA print];NSLog(@" - ");[vecB print];NSLog(@" = ");result = [vecA sub: vecB];[result print];// free memory[vecA release];[vecB release];[result release]; return 0;}其中result = [vecA add: vecB] 中的add: 是Vector類原有的方法, result = [vecA sub: vecB] 中的sub: 是Vector分類添加的方法。
分類是在Java和C++等物件導向的語言中沒有的概念, 分類本質上是通過Objective-C的動態綁定而實現的。通過分類使用能夠達到比繼承更好的效果。
從上面這個例子可以看到,分類提供了一種簡單的方式,
用它可以將類的定義模組化到相關方法的組或分類中。
它還提供了擴充現有類定義的簡便方式,並且不必訪問類的原始碼,也不需要建立子類。
#import "Fraction.h"
@interface Fraction (MathOps)
-(Fraction *) add: (Fraction *) f;
-(Fraction *) mul: (Fraction *) f;
-(Fraction *) sub: (Fraction *) f;
-(Fraction *) div: (Fraction *) f;
@end
注意,這既是介面部分的定義,也是現有介面部分的擴充。
因此,必須包括原介面部分,這樣編譯器就知道Fraction類。
按照慣例,作為分類的.h和.m檔案的基本名稱是由類的名稱緊接著分類的名稱。
例如:FractionMathOps.m;
一些程式員使用符號“+”來分隔類和分類的名稱,比如Fraction+MathOps.h。不過不建議這樣命名。
類的擴充:
建立一個未命名的分類,且在括弧“()”之間不指定名字,這是一種特殊的情況。
這種特殊的文法定義為類的擴充。
未命名類中聲明的方法需要在主實現地區實現,而不是在分離的實現地區中實現。
未命名分類是非常有用的,因為它們的方法都是私人的。
如果需要寫一個類,而且資料和方法僅供這個類本身使用,未命名類比較合適。
關於分類的注意事項:
分類可以覆寫該類中的另一個方法,但是通常認為這種做法是做虐的設計習慣。所以需要注意:
第一、覆寫了一個方法後,再也不能訪問原來的方法。(如果確實需要覆寫方法,正確的選擇可能是建立子類。)
第二、通過使用分類添加新的方法來擴充類不僅僅影響這個類,同時也會影響它的所有子類。
第三、對象/分類命名對必須是唯一的。因為大家使用的名稱空間是程式碼、庫、架構和外掛程式共用的。
協議(Protocol ) 與Java的Interface(介面 )或者C++的純虛類相同,就是用來聲明介面的。協議只是定義了方法的列表,協議不負責實現方法,目的是讓別的類來實現。
以Graphics協議為例:Graphics中定義了onDraw方法, 但是我們仔細分析一下onDraw方法不能實現的, 作為Graphics(幾何圖形)它無法知道它的子類如何繪製圖形, 它只能規定繪製圖名字為onDraw簽名和返回值等資訊, 但不能給出具體的實現, 因此Graphics(幾何圖形) 不應該設計成為類而應該設計成為協議。
@protocol Graphics
-(void) onDraw;
@end
協議只有介面部分, 沒有實現部分, 所以沒有m檔案, 關鍵字@protocol , 協議可以繼承別的協議, 協議中不能定義成員變數。
協議實作類別Ellipse
#import <Foundation/Foundation.h>#import "Graphics.h"@interface Ellipse:NSObject <Graphics> {}@end
#import "Ellipse.h"@implementation Ellipse-(void)onDraw {NSLog(@"繪製橢圓形");}@end協議實作類別Triangle
#import <Foundation/Foundation.h>#import "Graphics.h"@interface Triangle:NSObject <Graphics> {}@end
#import "Triangle.h"@implementation Triangle-(void)onDraw {NSLog(@"繪製三角形");}@end代碼說明:協議的實現是在類聲明的父類之後, 加上<Graphics>, 與類的單個繼承不同, 協議可以實現多個, 表示要實現這個協議, 如果有多個協議要實現用“,” 號分隔: <P1,P2>。
調用的main函數
#import <Foundation/Foundation.h>#import "Graphics.h"#import "Ellipse.h"#import "Triangle.h"int main (int argc, const char * argv[]) {id graphics;graphics = [[Ellipse alloc] init];[graphics onDraw];[graphics release];graphics = [[Triangle alloc] init];[graphics onDraw];[graphics release]; return 0;}
從上面的例子可以看出:
協議是多個類共用的一個方法列表。
協議中列出的方法沒有相應的實現,計劃由其他人來實現。
協議提供了一種方式,用指定的名稱定義一組多少有點相關的方法。
如果決定實現特定協議的所有方法,也就意味著要遵守(confirm to)或採用(adopt)這項協議。
定義一個協議很簡單:只要使用@protocol指令,然後跟上你給出的協議名稱。
例如:
@protocol NSCoping
- (id) copyWithZone: (NSZone *) zone;
@end
這裡再提到兩個重要的協議:
NSCopying
NSCoding
如果你的類採用NSCopying協議,則必須實現copyWithZone:的方法,
通過在@interface行的一對角括弧(<....>)內列出協議名稱,可以告知編譯器你正在採用的一個協議。
這項協議的名稱放在類名和它的父類名稱之後,
例如:
@interface AddressBook:NSObject <NSCoping>
如果你的類採用多項協議,只需把它們都列在角括弧中,並用逗號分開,例如:
@interface AddressBook:NSObject <NSCoping, NSCoding>
如果你定義了自己的協議,那麼不必由自己實現它。
如果一個類遵守NSCoping協議,則它的子類也遵守NSCoping協議
(不過並不意味著對該子類而言,這些方法得到了正確的實現)。
@required和@optional關鍵字
@protocol MyProtocol
- (void) requiredMethod;
@optional
- (void) anOptionalMethod;
- (void) anotherOptionalMethod;
@required
- (void) anotherRequiredMethod;
@end
注意,這裡使用了@optional指令。該指令之後列出的所有方法都是可選的。
@required指令之後的是需要的方法。
注意,協議不引用任何類,它是無類的(classless)。
任何類都可以遵循MyProtocol協議。
可以使用conformsToProtocol:方法檢查一個對象是否遵循某些協議。
例如:
if ([currentObject conformsToProtocol: @protocol (MyProtocol)] == YES) {
...
}
這裡使用的專用@protocol指令用於擷取一個協議名稱,併產生一個Protocol對象。
conformsToProtocol:方法期望這個對象作為它的參數。
通過在類型名稱之後的角括弧中添加協議名稱,可以藉助編譯器來檢查變數的一致性。
例如:
id <Drawing> currentObject;
這告知編譯器currentObject將包含遵守Drawing協議的對象。
如果這個變數儲存的對象遵守多項協議,則可以列出多項協議 ,
如以下代碼:
id <NSCoping, NSCoding> myDocument;
定義一項協議時,可以擴充現有協議的定義。
如:
@protocol Drawing3D <Drawing> Drawing3D 協議也採用了Drawing協議。
最後要說的是,分類也是可以採用一項協議。
和類名一樣,協議名必須是唯一的。
代理:
協議也是一種兩個類之間的介面定義。定義了協議的類可以看做是將協議定義的方法代理給了實現它們的類。
非正式(informal)協議:是一個分類,列出了一組方法但並沒有實現它們。
因此,非正式分類通常是為根類定義的。有時,非正式協議也稱為抽象(abstract)協議。
非正式協議實際上只是一個名稱下的一組方法。
聲明非正式協議的類自己並不實現這些方法,
並且選擇實現這些方法中的子類需要在它的介面部分重新聲明這些方法,
同時還要實現這些方法中的一個或多個。
注意:前面描述的@optional指令添加到了Objective-C 2.0語言中,用於取代非正式協議的使用。
合成對象:
可以定義一個類包含其他類的一個或多個對象。
這個新類的對象就是所謂的合成(composite)對象,因為它是由其他對象組成的。
子類依賴於父類,改變了父類有可能會使得子類中的方法不能工作。
objective-c:為何要用協議?
objective-c協議為不同的類提供公用的方法
協議定義了一組方法,而不提供具體實現。它定義了其它對象有責任實現的介面。當在自己的類中實現協議的方法時,使用者的類就是遵守這個協議的,協議聲明的方法可以被任何一個類實現。
通俗易懂地講
你寫了個類,你的這個類提供給我的類使用
我在我的類裡初始化了你的類執行個體,那麼好,你的方法我是可以調用的。
但是你的類會在達成某種條件的時候調用一個方法,這個方法你不知道要被誰實現,也無需你實現,你只是探出一個介面。
好吧,我會在按鈕按下時調用這個方法,但是要做什麼我不管,我也不知道,誰用的誰自己去實現。
那麼,在你的類裡定義協議
@protocol yourdelegate
-(void)dosomething
@end
定義id delegate屬性
並在你按鈕按下的方法裡
[delegate dosomething];
我的類裡使用你的協議
@interface 我的類:NSObject<yourdelegate>
並且在初始化你的類的時候, 你的類.delegate = self;
然後實現你的協議方法
-(void) dosomething
{做你想做的事
}
另外,你所說的:
我感覺協議,也是用來調用方法的,那麼執行個體化一個類,然後調用方法不就可以了嗎?
這句話在概念上就理解錯誤了,我想你可能對協議還不太熟悉,在什麼情況下需要使用協議。
你需要自己多試試幾個例子先熟悉協議的用法,然後思考,把邏輯理順了。
objective-c 什情況下必須使用協議
協議的設計使得類之間的關係解藕