iOS開發——使用delegate進行訊息傳遞
iOS開發中,介面之間傳遞訊息或者資料是最基本的一種需求,我們可以使用很多方法來實現這種需求,比如在標頭檔中暴露一個屬性、比如使用notification等等。今天我們要來介紹另一種方式:使用delegate傳遞訊息。
delegate稱為委託,同時也是一種代理設計模式。使用委託避免了類與類的耦合度過高。利用委託賦值更加安全可控,委託賦值在不暴露自己類的屬性的前提下就可以對自己進行賦值。
在iOS中委託通過協議@protocol來實現,在協議中所聲明的方法,不需要在協議中有實現的方法,是由它的實作類別來實現(該類需要在標頭檔中包含<協議名>)。“協議”和“委託”是兩個不一樣的概念,因為我們在看代碼的時候總是會看到他們出現在一起。但不能說,delegate就是protocol,delegate是一種架構模式。舉個例子什麼是“委託”:比如我在開車,但是開車不能看簡訊,所以我就叫車裡的朋友幫我看簡訊,這樣我就可以正常的開車了。當朋友看完簡訊內容後,就告訴我是什麼內容。這就是委託,朋友就是我的委派物件。“協議”protocol是用來定義對象的屬性和行為,類似於C++中的虛函數。其中有兩個關鍵字@required和@optional,前者表示必須要實現的方法,後者是可選的方法。
使用delegate常用在兩種情況下:
*兩個類之間的傳值,類A調用類B的方法,類B在執行過程中遇到問題通知類A;
*控制器(ViewController)與控制器(ViewController)之間的傳值,從A跳轉到B,再從B返回到A時需要通知A更新UI或者是做其他的事情,這時候就用到了代理(delegate)傳值。
tips:如果沒有在@protocol中顯式的寫上@required或者@optional,那麼預設就是@required(必需的)的。為了不引起歧義,最好顯式的寫上@required或@optional。
通常一個delegate的使用過程需要經過5步:
(1)建立一個delegate;
(2)委託者聲明一個delegate;
(3)委託者調用delegate內的方法(method);
(4)被委託者設定delegate,以便被委託者調用;
(5)被委託者實現delegate所定義的方法;
下面我通過1個執行個體來示範使用delegate傳遞資料,代碼上傳至:https://github.com/chenyufeng1991/iOS-Delegate 中的Delegate01.
(1)首先程式是基於storyboard來構建的,介面布局如下:在介面返回的時候將會傳遞資料。
。
(2)建立一個Protocol,作為我們的協議,裡面可以聲明一些方法。
。
ProtocolDelegate裡面的實現如下:
#import //實現一個protocol;/** * 可以理解為Java中的介面interface; */@protocol ProtocolDelegate // 必須實現的方法@required- (void)execute;// 可選實現的方法@optional- (void)function1;- (void)function2;- (void)function3;@end
(3)我首先來實現第二個介面,在第二個介面的標頭檔.h中需要聲明一個protocol,在該protocol中聲明的方法可以在第一個介面中實現,並在第二個介面中進行調用。這是資料傳遞的關鍵。
#import // 建立一個協議,協議的名字一般是由“類名+Delegate”/** * 在該協議中聲明的方法,在第一個介面中實現,在該介面中調用; */@protocol ViewControllerBDelegate @required- (void)sendValue:(NSString *)value;@end@interface ViewControllerB : UIViewController//這裡的delegate要設定在.h中;@property (weak, nonatomic) id delegate;@end
(4)在第二個介面的實現檔案中實現如下:在點擊返回鍵的時候調用協議中的方法,
#import "ViewControllerB.h"//第二個介面;@interface ViewControllerB ()@property (strong, nonatomic) IBOutlet UITextField *textField;@end@implementation ViewControllerB- (IBAction)backAction:(id)sender{ //介面跳轉的時候調用該方法; [self.delegate sendValue:self.textField.text]; // 通知執行協議方法 //返回傳遞訊息; [self.navigationController popViewControllerAnimated:YES];}@end
(5)下面來實現第一個介面ViewController.m:
#import "ViewController.h"#import "ProtocolDelegate.h"#import "ViewControllerB.h"//第一個介面;@interface ViewController () @end@implementation ViewController//因為是基於storyboard的segue來構建,當介面跳轉時,自動回調該方法;- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{ ViewControllerB *vc = segue.destinationViewController; [vc setDelegate:self];}// 這裡實現B控制器的協議方法- (void)sendValue:(NSString *)value{ UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"成功" message:value delegate:nil cancelButtonTitle:@"確定" otherButtonTitles:nil, nil]; [alertView show];}//該方法是ProtocolDelegate中的@required方法;- (void)execute{ }- (IBAction)pressed:(id)sender { NSLog(@"1111");}@end
(6)運行效果如下:
。
。
上面的demo是基於storyboard。由於有些項目是基於nib檔案構建的,我下面來示範一下使用nib檔案時的注意點。
(1)刪除原來的main.storyboard,新增兩個nib檔案,分別綁定對應的ViewController。我推薦使用navigationBar,所以在AppDelegate.m中的實現如下:
#import "AppDelegate.h"#import "ViewController.h"@interface AppDelegate ()@end@implementation AppDelegate- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; self.window.rootViewController = [[ViewController alloc] init]; self.naviController = [[UINavigationController alloc] initWithRootViewController:self.window.rootViewController]; [self.window addSubview:self.naviController.view]; [self.window makeKeyAndVisible]; return YES;}@end
(2)在第二個介面中主要修改如下,刪除prepareForSegue方法,在按鈕的點擊方法中實現:
- (IBAction)pressed:(id)sender { //如果項目本身是根據nib檔案構建的,就使用下面的方式綁定delegate,並跳轉; ViewControllerB *vb = [[ViewControllerB alloc] initWithNibName:@"ViewControllerB" bundle:[NSBundle mainBundle]]; vb.delegate = self; [self.navigationController pushViewController:vb animated:true];}
然後運行效果同上。不同之處也就是在ViewController綁定delegate處有不同。
總結,Delegate委託屬於程式架構層面上,是相對比較高的層次,如果我們能夠熟練使用delegate,就能有效降低程式耦合度,提高代碼可讀性。!