高見龍

iOS app/Ruby/Rails Developer & Instructor, 喜愛非主流的新玩具 :)

Protocol in Objective-C

image

前面提到了OOP的繼承,但不像C++可以有多重繼承,Objective-C是單一繼承的,如果想要做到一個類別同時擁有多種型別的能力,可以透過實作其它型別的interface來達成這個目的。在Java/AS3是用”interface”這個關鍵字,在Objective-C則是用”@protocol”。(有寫過Java/AS3的要特別注意不要把interface跟protocol搞混了,在Objective-C的interface等於Java/AS3的class,而protocol則是相當於interface)

直接來看看要怎麼做吧。如果你要新增一個自定的protocol的話,可以直接在你的專案裡新增一個protocol檔:

image

當然,你要全部寫在一起也沒人反對,只是為了模組化以及以後的可重複使用考量,建議獨立出來另外寫。新增完成之後(它是一個header檔),就可以開始來寫了,程式碼如下:

1
2
3
4
5
6
@protocol Drawable

-(void) draw;
-(void) changeColor;

@end

在Objective-C裡的protocol是用@protocol這個語法來定義的。在上面這段程式碼裡,我放了兩個方法,但沒有寫內容。接下來如果我要實作自這個protocol的話,所有定義在@protocol裡的方法都得實作出來。另外,在Objective-C 2.0之後加了@required@optional的語法,可以讓你設定這個method是不是必需一定要實作的項目。用法如下:

1
2
3
4
5
6
7
8
9
10
@protocol Drawable

@required
-(void) draw;
-(void) changeColor;

@optional
-(void) whateverMethod;

@end

如果沒特別標明的,預設是@required。如果你要實作這個protocol的話,照英文字面來看,@required的部份是規定要實作的,@optional的話就隨你高興了。要注意的是@required跟@optional這兩個語法的影響範圍,是從它以下所有的method都會被影響,直到另一個directive或是@end為止,所以如果你要省略@required的話,記得那些method要寫在@optional前面。接下來來看看要怎麼實作這個protocol:

1
2
3
4
5
6
7
8
9
10
#import <Cocoa/Cocoa.h>
#import "Drawable.h"

@interface Book : NSObject <Drawable>
{
  int price;
}
@property int price;

@end

實作protocol的方法就是用”<>”標記,裡面放protocol的名稱。並不限定只能實作一個protocol,如果要實作多個protocol的話,則是用逗點分開:

1
@interface Book : NSObject <Drawable, Openable>

因為到目前為止,我們都還沒實作那個protocol裡定義的方法,所以這時候如果直接按下Build的話,就會跳出警告訊息:

image

接著來把該做的填一填吧。因為在protocol的地方已經有定義好了方法,所以在@interface的地方就不用再特別寫一次,只要在@implementation裡補上該實作的方法就行了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// --------------
// interface
// --------------
#import <Cocoa/Cocoa.h>
#import "Drawable.h"

@interface Book : NSObject <Drawable>
{
  int price;
}
@property int price;

@end

// --------------
// implementation
// --------------
#import "Book.h"
@implementation Book

@synthesize price;

// 實作方法draw
-(void) draw
{
  NSLog(@"draw me!");
}

// 實作方法changeColor
-(void) changeColor
{
  NSLog(@"change color!");
}

@end

如果你實作了所有@required的方法的話,則稱為遵守(conform)或採納(adopt)這個protocol(硬翻成中文還是覺得怪怪的,還是英文比較簡潔直接)。若要檢查某物件是否有乖乖遵守某個protocol的規定:

1
2
3
4
5
Book *book = [[Book alloc] init];
if ([book conformsToProtocol:@protocol(Drawable)] == YES)
{
  NSLog(@"the book is conform to Drawable protocol");
}

protocol本身也可以像一般類別的繼承,例如:

1
2
3
4
5
6
7
@protocol A
-(void) methodA;
@end

@protocol B <A>
-(void) methodB;
@end

這時如果你要實作protocol B,則methodA跟methodB都需要實作。

另外,你也可以把protocol拿來當一般的型別定義來用,例如:

1
id <Drawable> some_object;

表示說這個some_object是個有實作Drawable這個protocol的物件,在編譯階段就可以先做型別檢查。當然也可以一次多個,一樣用逗點分開:

1
id <Drawable, Openable> some_object;

上面提到的這種用@protocol來定義方法的,稱做formal protocol,從名字看大概猜得出來一定也有叫做informal protocol的東西,不過這個會在category的部份再做說明。

Comments