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
#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
#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
。
。
上面的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
- (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,就能有效降低程序耦合度,提高代碼可讀性。!