協(xié)議
概念
協(xié)議是方法列表。在實際應(yīng)用中通過協(xié)議來規(guī)定代理雙方的行為。
聲明類的協(xié)議
協(xié)議就是定義公共接口的地方,只要遵守協(xié)議,就等于在頭文件中定義了這些方法,只要實現(xiàn)就行了。
所以,你可以定義一個協(xié)議,然后定義一個類遵循這個協(xié)議,在這個類的.m文件里實現(xiàn)這個協(xié)議中的方法。這樣不需要在.h中聲明,也可以在其他類中調(diào)用該類對應(yīng)協(xié)議中的方法。如下:
- 定義協(xié)議
#import <Foundation/Foundation.h>
@class HWWashDevice;
@protocol HWWashDeviceParserProtocol <NSObject>
@required
- (void)hw_ParseWashDevice:(HWWashDevice *)device;
@end
- 對應(yīng)方法中實現(xiàn)
@implementation HWDeviceParser
+ (instancetype)defaultParser
{
HWDeviceParser *parser = [[HWDeviceParser alloc] init];
return parser;
}
- (void)hw_ParseWashDevice:(HWWashDevice *)device
{
//解析洗衣機屬性
...
...
...
}
- 對該方法的調(diào)用
@interface HWWashDevice ()
@property (strong, nonatomic) id<HWWashDeviceParserProtocol> attributeparser;
@end
- (void)setUp
{
self.attributeparser = [HWDeviceParser defaultParser];
...
}
/**
* 設(shè)備屬性狀態(tài)變化
*
* @param device 設(shè)備對象
* @param attributes 屬性發(fā)生變化的集合
*/
- (void)device:(uSDKDevice *)device didUpdateVlaueForAttributes:(NSArray<uSDKDeviceAttribute *> *)attributes
{
NSLog(@"todo:%@", attributes);
[super device:device didUpdateVlaueForAttributes:attributes];
之所以有這樣的設(shè)計,是因為要將共同的行為抽象出來:不同的類有不同的作用和特征,這也是面向?qū)ο蟮奶攸c,但是即使千差萬別,還是會有某些相似點的,這些相似的地方就可以抽象出來做成協(xié)議。
其關(guān)系如下圖:

那么我們就可以將各個類的協(xié)議統(tǒng)一放在一個協(xié)議類里(例如把不同種類的菜都放在做菜協(xié)議里),不同的類遵循不同的協(xié)議(不同的廚師完成不同的具體菜品),這樣在實現(xiàn)功能時我們只需在協(xié)議里尋找對應(yīng)方法(具體菜品),然后找到遵循此協(xié)議的類(菜品對應(yīng)的廚師),就可以實現(xiàn)具體功能,使代碼邏輯更加清晰。
在一個完全組件化的項目中,關(guān)系如下圖。

定義一個基礎(chǔ)協(xié)議,協(xié)議中包含很多相關(guān)方法。例如菜單協(xié)議,內(nèi)包含很多具體菜名。然后有若干的類(廚師),遵循該協(xié)議,實現(xiàn)部分對應(yīng)方法。使用字典的方式進行組件化注冊。在公共部分,根據(jù)協(xié)議找到實現(xiàn)的Class。并使用class實例執(zhí)行方法。
組件中只需要開放protocol與service即可,其他的所有內(nèi)容在內(nèi)部實現(xiàn),不做開放。
而公共部分只需要找到需要的protocol,即可實現(xiàn)對應(yīng)方法。
實例
-
協(xié)議部分
MSViewModelServicesImpl遵循MSViewModelServices遵循MSNavigationProtocol
定義方法若干:/// Pushes the corresponding view controller. /// /// Uses a horizontal slide transition. /// Has no effect if the corresponding view controller is already in the stack. /// /// viewModel - the view model /// animated - use animation or not - (void)pushViewModel:(MSViewModel *)viewModel animated:(BOOL)animated; /// Pops the top view controller in the stack. /// /// animated - use animation or not - (void)popViewModelAnimated:(BOOL)animated; /// Pops until there's only a single view controller left on the stack. /// /// animated - use animation or not - (void)popToRootViewModelAnimated:(BOOL)animated; /// Present the corresponding view controller. /// /// viewModel - the view model /// animated - use animation or not /// completion - the completion handler - (void)presentViewModel:(MSViewModel *)viewModel animated:(BOOL)animated completion:(VoidBlock)completion; /// Dismiss the presented view controller. /// /// animated - use animation or not /// completion - the completion handler - (void)dismissViewModelAnimated:(BOOL)animated completion:(VoidBlock)completion; /// Reset the corresponding view controller as the root view controller of the application's window. /// /// viewModel - the view model - (void)resetRootViewModel:(MSViewModel *)viewModel; -
實現(xiàn)部分:
傳統(tǒng)方式遵循該協(xié)議,并實現(xiàn)協(xié)議對應(yīng)方法。
RAC 實現(xiàn)方式如下:// 不需要遵循該協(xié)議。而是創(chuàng)建時傳入該協(xié)議使之持有。 - (instancetype)initWithServices:(id<MSViewModelServices>)services;初始化時注冊代理實現(xiàn)方法。
- (void)registerNavigationHooks { @weakify(self) // 使用RAC監(jiān)測方法調(diào)用,當(dāng)方法調(diào)用時運行block內(nèi)部代碼,實際上就是協(xié)議方法的具體實現(xiàn)。 [[(NSObject *)self.services rac_signalForSelector:@selector(pushViewModel:animated:)] subscribeNext:^(RACTuple *tuple) { @strongify(self) MSViewController *topViewController = (MSViewController *)[self.navigationControllers.lastObject topViewController]; if (topViewController.tabBarController) { topViewController.snapshot = [topViewController.tabBarController.view snapshotViewAfterScreenUpdates:NO]; } else { topViewController.snapshot = [[self.navigationControllers.lastObject view] snapshotViewAfterScreenUpdates:NO]; } UIViewController *viewController = (UIViewController *)[MSRouter.sharedInstance viewControllerForViewModel:tuple.first]; [self.navigationControllers.lastObject pushViewController:viewController animated:[tuple.second boolValue]]; }]; } -
調(diào)用
最后 viewModel 中再持有一份這個協(xié)議。
利用如下方式調(diào)用:[self.viewModel.services pushViewModel:model animated:YES];
協(xié)議用于做封裝
比如現(xiàn)在有一個MeetingUser的類,其中的有很多屬性方法,只有部分屬性及部分方法暴露出去。
//interface.h中
- (void)getUser:(id<CustomProtocol>)user;
定義CustomProtocol協(xié)議,聲明準(zhǔn)備暴露出去的屬性(屬性的get方法)及方法。
@protocol CustomProtocol
- (NSString *)getPropertyA;
- (NSString *)getPropertyB;
- (void)methodA;
原有的MeetingUser遵循該協(xié)議,實現(xiàn)其定義的方法(屬性等原方法不變)。
在getUser中直接返回MeetingUser對象即可。對外來說只是一個id類型遵循CustomProtocol的對象。可以使用該對象直接執(zhí)行協(xié)議中方法。
以這樣的方式實現(xiàn)封裝。
代理
概念
為其他對象提供一種代理以控制對這個對象的訪問。
代理模式分為三部分:委托方,代理方,協(xié)議。
委托方:tableview
協(xié)議:UITableviewdelegate,UITableviewDatasource
代理方:遵循協(xié)議的controller。
可以使用tableview.delegate=self來使本控制器來做代理方。
也可以新建一個類,例如tableviewDelegate類A。實例化該類,tableview.delegate = A.init。使A的實例作為代理方。在A類中實現(xiàn)協(xié)議中的方法。
id<UItableviewDelegate> delegate
一個任意類型可以遵循該協(xié)議的具體對象。該實例可以作為變量,可以作為參數(shù)傳遞。該實例必然可以執(zhí)行協(xié)議中的方法。
總結(jié)
tableview 定義了一個協(xié)議 tableviewdelegate 和tableviewdatasource。tableview作為委托方,制定了一個方法列表,找人幫它實現(xiàn)。viewcontroller使用了tableview,那么必須要為tableview指定一個代理方,可以是自己,也可以再找一個。