协议与代理

协议

概念

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

声明类的协议

协议就是定义公共接口的地方,只要遵守协议,就等于在头文件中定义了这些方法,只要实现就行了。
所以,你可以定义一个协议,然后定义一个类遵循这个协议,在这个类的.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指定一个代理方,可以是自己,也可以再找一个。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容