协议与代理

协议

概念

协议是方法列表。在实际应用中通过协议来规定代理双方的行为

声明类的协议

协议就是定义公共接口的地方,只要遵守协议,就等于在头文件中定义了这些方法,只要实现就行了。
所以,你可以定义一个协议,然后定义一个类遵循这个协议,在这个类的.m文件里实现这个协议中的方法。这样不需要在.h中声明,也可以在其他类中调用该类对应协议中的方法。如下:

  • 定义协议
#import <Foundation/Foundation.h>
@class HWWashDevice;
@protocol HWWashDeviceParserProtocol <NSObject>
@required
- (void)hw_ParseWashDevice:(HWWashDevice *)device;
@end
  • 对应方法中实现
@implementation HWDeviceParser
+ (instancetype)defaultParser
{
    HWDeviceParser *parser = [[HWDeviceParser alloc] init];
    return parser;
}
- (void)hw_ParseWashDevice:(HWWashDevice *)device
{
    //解析洗衣机属性
    ...
    ...
    ...
}
  • 对该方法的调用
@interface HWWashDevice ()
@property (strong, nonatomic) id<HWWashDeviceParserProtocol> attributeparser;
@end

- (void)setUp
{
    self.attributeparser = [HWDeviceParser defaultParser];
    ...
}

/**
 *  设备属性状态变化
 *
 *  @param device     设备对象
 *  @param attributes 属性发生变化的集合
 */
- (void)device:(uSDKDevice *)device didUpdateVlaueForAttributes:(NSArray<uSDKDeviceAttribute *> *)attributes
{
    NSLog(@"todo:%@", attributes);
    [super device:device didUpdateVlaueForAttributes:attributes];
   

之所以有这样的设计,是因为要将共同的行为抽象出来:不同的类有不同的作用和特征,这也是面向对象的特点,但是即使千差万别,还是会有某些相似点的,这些相似的地方就可以抽象出来做成协议。
其关系如下图:

协议关系.jpeg

那么我们就可以将各个类的协议统一放在一个协议类里(例如把不同种类的菜都放在做菜协议里),不同的类遵循不同的协议(不同的厨师完成不同的具体菜品),这样在实现功能时我们只需在协议里寻找对应方法(具体菜品),然后找到遵循此协议的类(菜品对应的厨师),就可以实现具体功能,使代码逻辑更加清晰。

在一个完全组件化的项目中,关系如下图。

组件化的协议实现方式.jpeg

定义一个基础协议,协议中包含很多相关方法。例如菜单协议,内包含很多具体菜名。然后有若干的类(厨师),遵循该协议,实现部分对应方法。使用字典的方式进行组件化注册。在公共部分,根据协议找到实现的Class。并使用class实例执行方法。

组件中只需要开放protocol与service即可,其他的所有内容在内部实现,不做开放。
而公共部分只需要找到需要的protocol,即可实现对应方法。

实例
  • 协议部分
    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;
    
  • 实现部分:
    传统方式遵循该协议,并实现协议对应方法。
    RAC 实现方式如下:

    // 不需要遵循该协议。而是创建时传入该协议使之持有。
    - (instancetype)initWithServices:(id<MSViewModelServices>)services;
    

    初始化时注册代理实现方法。

    - (void)registerNavigationHooks {
      @weakify(self)
      // 使用RAC监测方法调用,当方法调用时运行block内部代码,实际上就是协议方法的具体实现。
      [[(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]];
       }];
    }
    
  • 调用
    最后 viewModel 中再持有一份这个协议。
    利用如下方式调用:

    [self.viewModel.services pushViewModel:model animated:YES];
    
协议用于做封装

比如现在有一个MeetingUser的类,其中的有很多属性方法,只有部分属性及部分方法暴露出去。

//interface.h中
-  (void)getUser:(id<CustomProtocol>)user;

定义CustomProtocol协议,声明准备暴露出去的属性(属性的get方法)及方法。

@protocol  CustomProtocol

- (NSString *)getPropertyA;
- (NSString *)getPropertyB;
- (void)methodA;

原有的MeetingUser遵循该协议,实现其定义的方法(属性等原方法不变)。
getUser中直接返回MeetingUser对象即可。对外来说只是一个id类型遵循CustomProtocol的对象。可以使用该对象直接执行协议中方法。
以这样的方式实现封装。

代理

概念

为其他对象提供一种代理以控制对这个对象的访问。
代理模式分为三部分:委托方,代理方,协议。

委托方:tableview
协议:UITableviewdelegate,UITableviewDatasource
代理方:遵循协议的controller。

可以使用tableview.delegate=self来使本控制器来做代理方。
也可以新建一个类,例如tableviewDelegate类A。实例化该类,tableview.delegate = A.init。使A的实例作为代理方。在A类中实现协议中的方法。

id<UItableviewDelegate> delegate
一个任意类型可以遵循该协议的具体对象。该实例可以作为变量,可以作为参数传递。该实例必然可以执行协议中的方法。

总结

tableview 定义了一个协议 tableviewdelegate 和tableviewdatasource。tableview作为委托方,制定了一个方法列表,找人帮它实现。viewcontroller使用了tableview,那么必须要为tableview指定一个代理方,可以是自己,也可以再找一个。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,776评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,527评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,361评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,430评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,511评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,544评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,561评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,315评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,763评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,070评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,235评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,911评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,554评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,173评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,424评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,106评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,103评论 2 352

推荐阅读更多精彩内容