天天看點

objective-C中的接口與泛型

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

不過在obj-c中有二個與之接近的概念"非正式協定(interface)"與"正式協定(protocol)"。非正式協定在obj-c中的關鍵字雖然也是interface,但是這個跟c#中的接口(interface)并不完全相同。

回憶一下前面學過的内容,我們定義一個類Sample時,總是會先生成一個Sample.h,代碼如下:

#import <Foundation/Foundation.h>

@interface Sample : NSObject {

}

-(void) HelloWorld;

@end
      

它表明Sample類中,約定了"應該"有一個名為HelloWorld的方法(注:我這裡說的是應該,而不是必須),它隻是一種君子協定。

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

objective-C中的接口與泛型

上圖中的提示:Incomplete implementation of class "Sample". 意為:Sample類并未完全實作interface中約定的方法。

這就是obj-c中的協定跟c#中的接口不一樣的地方:在c#中接口是強制必須實作的,否則編譯這一關就過不了,而obj-c雖然在編譯時會警告,但是最終能編譯通過。

正式協定(protocol)

其實就是非正式協定(interface)換了一種寫法而已,看上去更正規一些,語義上更強烈一些:要求采用該協定的類,"必須"實作協定中約定的方法。但是比較娛樂的是,即使是号稱正式協定,編譯器在編譯時,遇到不守規矩的情況,仍然隻是給出警告。(當然正式協定也有它存在的意義,後面會提到)

這裡我們定義一個IQuery的協定

IQuery.h

@protocol IQuery

-(void) Query:(NSString*) sql;

@end
      

除了把關鍵字@interface換成了@protocol,其它的基本上沒變化。下面定義一個類DBQuery,并采用這個正式協定

DBQuery.h

#import <Foundation/Foundation.h>
#import "IQuery.h"

@interface DBQuery : NSObject<IQuery> {

}

@end
      

注意這裡的DBQuery:NSObject<IQuery>,它表明DBQuery繼承自NSObject,同時要實作接口IQuery。

DBQuery.m

#import "DBQuery.h"

@implementation DBQuery

-(void) Query:(NSString *)sql
{
  NSLog(@"Query is called. sql:%@",sql);
}

@end
      

當然,如果在DBQuery.m中不實作方法Query,也能編譯通過,隻是會收到一個警告。

也許到目前為止,你會覺得protocol跟interface比起來,都是類似的概念,protocol設計純屬多餘。其實不然,protocol存在的一個重要意義在于:

正式協定(protocol)可以将業務中的方法定義剝離出來,形成一個單獨的檔案,這跟傳統OO中的提取接口是不謀而合的。如果遇到二個系統需要交換資料,可以制定一套雙方都遵守的protocol,然後這二個系統中都把這個協定檔案添加到項目中,實作它即可。這一功能,非正式協定(@interface)就做不到。(不信大家可以把NSObject<IQuery>中的IQuery改成其它類的interface定義名稱試試,編譯根本通不過)

此外,obj-C 2.0中對正式協定還做了一些擴充,允許把正式協定中的方法辨別為“必須實作(@requied)”和“可選實作(@optional)”二類,如果協定中的方法被辨別為@optional,即使采用該協定的類不實作這些方法,編譯器也不會給出警告。這賦予了正式協定更多的靈活性。示例如下:

@protocol IQuery

@required
-(void) Query:(NSString*) sql;

@optional
-(void) HelloWorld; 

@end
      

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

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

objective-C中的接口與泛型

選擇protocol NSObject 繼續,會看到NSObject.h檔案中關于protocol NSObject的定義

objective-C中的接口與泛型

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

objective-C中的接口與泛型

從這裡可以看到,非正式協定的interface NSObject其實最終采用的還是正式協定protocol NSObject.

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

另一個話題泛型

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

#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) 長得很象? 

作者:菩提樹下的楊過