文章目錄
轉載地址:http://www.iloss.me/?p=545
協議分非正式協議和正式協議
先說說非正式協議
非正式協議在Obj-c中關鍵字雖然是interface,但是這個和C#中的借口並不完全相同。
回憶之前學過的內容,我們定義一個類Sample時,會產生一個Sample.h,代碼如下:
#import <foundation foundation.h>
@interface Sample:NSObject {
}
-(void) HelloWorld;
@end
它表明Sample類中,約定了應該有一個名為HelloWorld的方法(注:這裡說的時應該,而不是必須)
它只是一種君子協議。
如果我們在Sample.m中,並不遵守這個約定(即不實現這個方法),編譯器會給出警告。但是還時會編譯成功
中的提示:Incomplete implementation of class “Sample”. 意為:Sample類並未完全實現interface中約定的方法。
這就是obj-c中的協議跟c#中的介面不一樣的地方:在c#中介面是強制必須實現的,否則編譯這一關就過不了,而obj-c雖然在編譯時間會警告,但是最終能編譯通過。
正式協議(protocal)
其實就是非正式協議(interface)換了一種寫法而已,看上去更正
規一些,語義上更強烈一些:要求採用該協議的類,”必須”實現協議中約定的方法。但是比較娛樂的是,即使是號稱正式協議,編譯器在編譯時間,遇到不守規矩的
情況,仍然只是給出警告。(當然正式協議也有它存在的意義,後面會提到)
這裡我們定義一個IQuery的協議
IQuery.h
@protocol IQuery
-(void) Query:(NSString*) sql;
@end
除了把關鍵字@interface換成了@protocal,其它的基本上沒變化。下面定義一個類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,也能編譯通過,只是會收到一個警告。
也許到目前為止,你會覺得protocal跟interface比起來,都是類似的概念,protocal設計純屬多餘。其實不然,protocal存在的一個重要意義在於:
正
式協議(protocal)可以將業務中的方法定義剝離出來,形成一個單獨的檔案,這跟傳統OO中的提取介面是不謀而合的。如果遇到二個系統需要交換數
據,可以制定一套雙方都遵守的protocal,然後這二個系統中都把這個協議檔案添加到項目中,實現它即可。這一功能,非正式協議
(@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或protocal
選擇protocal NSObject 繼續,會看到NSObject.h檔案中關於protocal NSObject的定義
同樣的,你還可以看到interface NSObject的定義
從這裡可以看到,非正式協議的interface NSObject其實最終採用的還是正式協議protocal 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) 長得很象?
自己的理解:
其實協議就相當於Java 中的interface 或者C++ 中的virtual class
舉個例子說說:
首先我們聲明一個協議
@protocol MyProtocol
- (void)myProtocolMethod;
@end
@interface
TestA
{
TestB
<myProtocol>* pB; // 說明類B應該支援<遵守>myProtocol這個協議
}
@interface
TestB:
NSObject
<
myProtocol>{
}
現在我們看如果我們在TestA裡面昨晚了一件事情,然後要通知TestB。那就要通過myProtocol來實現,那通過pB我們就可以給myProtocol發通知,那TestB也就收到訊息了
下面再附上一個用協議實現回呼函數的例子,(網上轉來的,但可以協助我們理解)
在編寫android開發時,最常用的就是回呼函數。自己編寫回呼函數,實現動態載入資料,載入完資料之後就利用回呼函數通知給前台頁面,顯示相應資料的介面。在iphone中利用協議可以很容易的實現回呼函數,後台載入資料,然後顯示在前台頁面。
實現一個顯示文字為測試的視圖,然後經過3秒鐘測試文字變為回呼函數文字。相應的如下:
實現的代碼如下:
#import <UIKit/UIKit.h>
@protocol NoteDelegate //聲明協議
//回呼函數
-(void)messageCallBack:(NSString *)string;
@end
調用協議:
#import <Foundation/Foundation.h>
#import “NoteDelegate.h”
@interface ManagerMessage : NSObject {
id<NoteDelegate> *noteDelegate;
}
@property (nonatomic,retain) id<NoteDelegate> *noteDelegate; //@property先行編譯命令的作用是自動聲明屬性的 setter 和getter方法
-(void)startThread;
@end
#import “ManagerMessage.h”
@implementation ManagerMessage
@synthesize noteDelegate;
//開始一個線程
-(void)startThread
{
//啟動線程
[NSTimer scheduledTimerWithTimeInterval:3
target:self
selector:@selector(targetMethod:)
userInfo:nil
repeats:NO];
}
-(void)targetMethod:(NSString *)string
{
if (self.noteDelegate!=nil) {
//完成線程 調用回呼函數
[self.noteDelegate messageCallBack:@"回呼函數"]; //在這通過協議進行回調
}
}
@end
前台頁面實現:
#import “IphoneDeleteViewController.h”
#import “ManagerMessage.h”
@implementation IphoneDeleteViewController
@synthesize textView;
//回呼函數
-(void)messageCallBack:(NSString *)string
{
self.textView.text=string; //前台收到回調資訊 給了textView
}
- (void)viewDidLoad {
[super viewDidLoad];
self.textView.text=@”測試”;
ManagerMessage *message=[[ManagerMessage alloc] init];
//通知調用協議
message.noteDelegate=self;
[message startThread];
[message release];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)viewDidUnload {
self.textView=nil;
}
- (void)dealloc {
[self.textView release];
[super dealloc];
}
@end