[iOS] 代理委派(一)

  • 1691
  • 0
  • iOS
  • 2015-03-01

摘要:[iOS] 代理委派(一)

在[Xamarin.Android Navigation Drawer(ㄧ)]文章中提到.Net的委派機制,這篇文章紀錄一下委派機制在

iOS裡面是如何實踐。

可以參考下方連結。

http://www.dotblogs.com.tw/toysboy21/archive/2015/02/02/148352.aspx

在設計iOS的APP上,委派代理機制是常常被使用到的。在一個ViewController裡面新增UITableView的時候就會碰到。可以參考下方iOS UITableView的文章。

http://www.dotblogs.com.tw/toysboy21/archive/2013/11/19/130485.aspx

 

這邊一樣用訂Pizza的故事來說明iOS的委派機制結構。

首先想像,假如你到Pizza Hat去訂一個Pizza。你告訴店員Pizza好的時候,通知我來拿。我們把這兩個元件用類別圖來表示的話,在iOS的design pattern應該會長的像底下這樣。

 

實際上要去拿Pizza,這個Move方法是掛在使用者(User類別)身上。你可以這樣想像,User類別把

Move這個方法委派給店家來觸發執行。也就是實際上,我們告訴店家當Pizza出爐的時候,請通知我來拿Pizza。

協定(Protocol)就是interface。其用來當你要委派別人處理事情,別人至少要有能力處這件事的能力,或者是知道要怎麼樣才能觸發你去處理某件事情。這個部分就仰賴協定(Protocal)來規範。

白話文:當我要委派PizzaHat來通知我去拿Pizza,PizzaHat至少要有知道如何通知我的能力。

 

UITableView的委派機制

已UITableView來說,在iOS開發時使用到UITableView,會在程式裡面宣告底下的語法,這是說把

UItableView的事件處理以及資料處理委派給自己(ViewController)。

self.UITableView.delegate = self;
self.UITableView.datasource = self;

那外部物件要怎麼得知與呼叫委派物件的方法呢?主要是依賴委派物件去實作某些特定的協定。

例如引用UITableView的ViewController.h檔案裡面必須要實作UITableViewDataSource與UITableViewDelegate兩個協定。當實作這兩個協定之後,在ViewController.m檔案中就要去實作相對應的方法。

//有多少Section
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView

以及

//每個Section有多少Row
- (NSInteger)tableView:(UITableView *)tableViewnumberOfRowsInSection:(NSInteger)section

還有

//每個Row裡面的Cell資料
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

 

至於自己(self)已經被委派給UITableView.delegate還有UITableView.datasource。哪上述的三個方法什麼時候會被觸發呢?這就仰賴iOS底層幫我們處理掉了。所以我們不用去管怎麼被觸發的細節,只要專心在當這個事件被觸發的時候,我們要處理的常式。如同當按鈕事件被觸發時,我們只要專心地去管事件處理常式就好。

換個方式解釋,實際上你的手指頭在刷刷刷的滑動UITableView的時候,iOS幫我們處理了底層的一些判斷,並且去觸發了一些相對應的事件。而我們把整個UITableView Delegate給自己(self),所以我們只要在ViewController.m檔案去實作相對應的事件處理常式就可以。

 

建立Command Line專案

接著我們用一個簡單的範例來實踐iOS的委派。開啟Xcode新增一個Command Line Tool的專案。

 

 

宣告協定 Protocal (interface)

新增一個協定檔案,這個協定就是interface。在協定中定義了一個CallMeForTakePizza的方法。

PhoneCallProtocol.h

#import 
//定義一個Protocal
@protocol getnotificationDelegate 
@optional
- (void)callmefortakepizza:(NSString*)pizzatype;
@end

 

建立被委派的類別(PizzaHat)

建立一個類別檔案PizzaHat (Pizza店),在Pizza.h檔案宣告下方程式碼

首先再標頭檔案去引用協定檔案。

宣告一個delegate變數。這是一個存取子(Accessor)。回傳一個id<getnotificationDelegate>的物件指標,這邊指的就是外部物件(買Pizza的人)的記憶體位址。

Id本身可以把它看作是泛型物件的一種應用。而id<protocol>的宣告是指,這個物件必須要是有實作

<protocol>的物件。

例如id<getnotificationDelegate>這段描述的意思是,只你要傳入的物件必須是要實作getnotificationDelegate協定的物件。

另外宣告一個OnPizzaok方法。這個方法是用來呼叫外部物件的方法。用白話的方式來說,就是PizzaHat要去呼叫買Pizza的人走路來買Pizza時被觸發的方法。可以想像成Button 的TouchUpInside(click)事件。

PizzaHat.h 檔案

#import 
#import "PhoneCallProtocol.h"
@interface PizzaHat : NSObject
@property (nonatomic,assign) id delegate;
-(void) OnPizzaok;
@end

 

在PizzaHat.m檔案的implementation的執行區塊,實作OnPizzaOK的方法。在這個方法中,呼叫

delegate指標物件的CallmeForTakePizza的這個方法。意思是,當客人的Pizza完成的時候,請通知他來拿Pizza。

PizzaHat.m 檔案

#import 
#import "PizzaHat.h"

@implementation PizzaHat
@synthesize delegate;
-(void)OnPizzaok{
    [delegate callmefortakepizza:@"夏威夷Pizza"];
}
@end

 

建立委派的類別(買Pizza的人)

宣告一個買Pizza的人的類別,在標頭的地方引用PhoneCallProtocol.h的Protocol。

這個類別要實作getnotificationDelegate這個協定(介面)。

User.h

#import 
#import "PhoneCallProtocol.h"

@interface User : NSObject
@end

在User.m,因為在User.h檔案中實作了getnotificationDelegate這個協定。

所以就必須要去宣告getnotificationDelegate這個協定中所定義的callmefortakepizza這個方法的常式。

User.m

#import 
#import "User.h"
@implementation User
-(void)callmefortakepizza:(NSString *)pizzatype{
    NSLog(@"Ben先生,您的 %@ Pizza已經完成了",pizzatype);
}
@end

 

程式的撰寫

這個範例並不是在iOS專案下開發,是在終端機的模式下開發,所以在Main.m檔案中要宣告底下的程式。在int Main底下同時宣告並建立PizzaHat與User的物件實體。接著將user物件指派給

PizzaHat.delegate。然後呼叫[pizzaHat OnPizzaOK]方法。

main.m

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        PizzaHat *pizzaHat = [[PizzaHat alloc]init];
        User *user = [[User alloc]init];
        pizzaHat.delegate =  user;
        [pizzaHat OnPizzaok];
    }
    return 0;
}

 

執行這個程式,可以看見當呼叫了[pizzaHat OnPizzaok]方法時,實際上觸發了

[user callmefortakepizza]這一個方法。

 

範例程式下載:https://github.com/twbenlu/iOSDelegatePizzaHat