objective-C中的接口與泛型("非正式協議(interface)"與"正式協議(protocal)")

原文地址:http://www.cnblogs.com/yjmyzz/archive/2011/03/02/1969126.html

先承認我是標題黨,因爲在obj-c的世界中,官方根本沒有"接口"與"泛型"這樣的說法。

不過在obj-c中有二個與之接近的概念"非正式協議(interface)"與"正式協議(protocal)"。非正式協議在obj-c中的關鍵字雖然也是interface,但是這個跟c#中的接口(interface)並不完全相同。
回憶一下前面學過的內容,我們定義一個類Sample時,總是會先生成一個Sample.h,代碼如下:
1
2
3
4
5
6
7
8
9
#import <Foundation/Foundation.h>
 
@interface Sample : NSObject {
 
}
 
-(void) HelloWorld;
 
@end
它表明Sample類中,約定了"應該"有一個名爲HelloWorld的方法(注:我這裏說的是應該,而不是必須),它只是一種君子協定

如果我們在Sample.m中,並不遵守這個約定(即:不實現這個方法),編譯時xcode會給出警告,如下圖。但最後還是會編譯成功(即:編譯器對此是睜一隻眼閉一隻眼,默認了Sample類的這種不忠行爲)

上圖中的提示:Incomplete implementation of class "Sample". 意爲:Sample類並未完全實現interface中約定的方法。
這就是obj-c中的協議跟c#中的接口不一樣的地方:在c#中接口是強制必須實現的,否則編譯這一關就過不了,而obj-c雖然在編譯時會警告,但是最終能編譯通過。


正式協議(protocal)
其實就是非正式協議(interface)換了一種寫法而已,看上去更正規一些,語義上更強烈一些:要求採用該協議的類,"必須"實現協議中約定的方法。但是比較娛樂的是,即使是號稱正式協議,編譯器在編譯時,遇到不守規矩的情況,仍然只是給出警告。(當然正式協議也有它存在的意義,後面會提到)
這裏我們定義一個IQuery的協議
IQuery.h
1
2
3
4
5
@protocol IQuery
 
-(void) Query:(NSString*) sql;
 
@end
除了把關鍵字@interface換成了@protocal,其它的基本上沒變化。下面定義一個類DBQuery,並採用這個正式協議
DBQuery.h
1
2
3
4
5
6
7
8
#import <Foundation/Foundation.h>
#import "IQuery.h"
 
@interface DBQuery : NSObject<IQuery> {
 
}
 
@end
注意這裏的DBQuery:NSObject<IQuery>,它表明DBQuery繼承自NSObject,同時要實現接口IQuery。
DBQuery.m
1
2
3
4
5
6
7
8
9
10
#import "DBQuery.h"
 
@implementation DBQuery
 
-(void) Query:(NSString *)sql
{
    NSLog(@"Query is called. sql:%@",sql);
}
 
@end
當然,如果在DBQuery.m中不實現方法Query,也能編譯通過,只是會收到一個警告。
也許到目前爲止,你會覺得protocal跟interface比起來,都是類似的概念,protocal設計純屬多餘。其實不然,protocal存在的一個重要意義在於:
正式協議(protocal)可以將業務中的方法定義剝離出來,形成一個單獨的文件,這跟傳統OO中的提取接口是不謀而合的。如果遇到二個系統需要交換數據,可以制定一套雙方都遵守的protocal,然後這二個系統中都把這個協議文件添加到項目中,實現它即可。這一功能,非正式協議(@interface)就做不到。(不信大家可以把NSObject<IQuery>中的IQuery改成其它類的interface定義名稱試試,編譯根本通不過)
此外,obj-C 2.0中對正式協議還做了一些擴展,允許把正式協議中的方法標識爲“必須實現(@requied)”和“可選實現(@optional)”二類,如果協議中的方法被標識爲@optional,即使採用該協議的類不實現這些方法,編譯器也不會給出警告。這賦予了正式協議更多的靈活性。示例如下:
1
2
3
4
5
6
7
8
9
@protocol IQuery
 
@required
-(void) Query:(NSString*) sql;
 
@optional
-(void) HelloWorld;
 
@end

有了@optional關鍵字以後,其實“非正式協議”在語義上完全可以被“正式協議”所取代,事實上Cocoa中的非正式協議都在逐漸被標有@optional方法的正式協議所代替。

如果你在XCode的代碼中,選中NSObject,右擊-->Jump to Definition,會發現NSObject其實就是一個interface或protocal

選擇protocal NSObject 繼續,會看到NSObject.h文件中關於protocal NSObject的定義

同樣的,你還可以看到interface NSObject的定義

從這裏可以看到,非正式協議的interface NSObject其實最終採用的還是正式協議protocal NSObject.

也就是說,在obj-c的OO世界中,身爲萬物之祖的NSObject其實也就一個"正式協議”,所以從NSObject派生出的所有類,都只是在遵守一個或多個協議而已。


另一個話題泛型

在obj-c中,一切皆爲指針。前面的學習中,我們已經接觸到了一種特殊的類型id,它可以認爲是一種特殊的指針:可以指向任何類型的對象。id 再加上正式協議,能夠達到形似c#中泛型的效果(注:只是形似,並非神似)

1
2
3
4
5
6
7
8
9
#import <Foundation/Foundation.h>
#import "IQuery.h"
 
@interface DBQuery : NSObject<IQuery> {  
 
}
 
-(void) test:(id<IQuery>) obj;
@end

注意這裏的 -(void) test:(id<IQuery>) obj; 這表明test方法接受一個任意類型的對象做爲參數,但是該參數對象必須實現接口IQuery(也可以說成該參數對象必須採用正式協議IQuery),是不是跟c#中的

void test(List<IQuery> obj) 長得很象?

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章