• iOS开发总结——协议代理的认识


    1.前言

            自今年5月底正式转iOS之后,天天get新技能,很多技能在脑子里回旋不吐不快,所以,写点东西整理一下。先从协议代理开始。

    2.协议方法的声明

    @protocol EventMenuBarDelegate <NSObject>
    
    - (void)delegateShouldDoWhenMenuButtonTapped:(UIButton *)button;
    
    @end

    以上代码意思是,利用@protocol 指令声明协议名EventMenuBarDelegate,并遵从NSObject协议,在协议中声明了一个方法

    - (void)delegateShouldDoWhenMenuButtonTapped:(UIButton *)button,由于没有用@option 指令声明,所以服从该EventMenuBarDelegate的对象必须实现delegateShouldDoWhenMenuButtonTapped: 这个方法。

    3.协议代理的认识

            协议中声明的方法,其实就是C++等面向对象的编程语言的抽象方法,不需要具体实现过程,但是必须要声明方法接口,以用于继承该类的子类进行适配,这就是设计模式中的适配器模式。在Objective-C中,子类通过遵从协议和继承超类以达到C++的多继承的效果。我认为这样的好处就是,多态。

    4.情景模拟与代码实现

            委托代理其实就是某个对象需要完成某个方法所找的另外一个对象。比如,我是个懒人,我不喜欢洗衣服,我找洗衣助理帮我洗,此时,助理就是我洗衣服的委托代理,并且必须遵从洗衣协议并实现洗衣服的过程。在助理洗完之后,我再作为洗衣助理的委托代理表示感谢,向助理说谢谢并付钱。(有点绕,需要脑洞大开)如果用程序实现就可以像下面一样写。其实,这个情景中,代码模拟了两个委托代理,一个是洗衣服的协议代理,另外一个是整个业务逻辑的协议代理。

    1)洗衣服的协议方法

    #import <Foundation/Foundation.h>
    #import "APPBLLDelegate.h"
     
    @protocol WashClothes <NSObject>
    
    - (void)washClothes:(int)num complete:(id<APPBLLDelegate>)delegate;
    
    @end

    代码声明了洗衣服的协议,遵从该协议的所有对象必须完成一件事根据传入的衣服数量洗衣服,洗完之后代理我(代理)再做出响应的处理。

    2)业务逻辑协议

    @protocol APPBLLDelegate <NSObject>
    
    - (void)didWashClothesSuccess:(BOOL)finished leftNum:(int)left;
    
    - (void)didWashClotheFailed:(NSString *)errorMsg;
    
    @end

    业务逻辑的协议中,声明了两个方法,一个是洗衣成功后我需要做的,另一个是洗衣未成功我需要做的。其实,这里完全可以抽象成任何事件的协议方法,换个名字就成。比如,一次网络请求响应,一次数据库的读库写库。

    而作为该业务逻辑的我(代理)而言,当洗衣助理把我当代理参数传入的时候,并不确定我的类型,仅作为一个id类型的对象指针传入,但是我必须遵循APPBLLDelegate协议,即

    (id<APPBLLDelegate>)delegate

    3)我(用户)的类代码实现

    头文件

    #import <Foundation/Foundation.h>
    #import "WashClothes.h"
    #import "APPBLLDelegate.h"
    
    @interface User : NSObject <APPBLLDelegate>
    
    @property int clothesNum;
    @property (weak, nonatomic) id<WashClothes> delegate;
    
    - (void)askAssistantForWashingClothes;
    
    @end

    我洗衣服的代理是助理,助理的类不一定是什么类,我不确定,所以用id类型来指代其类型(当然,如果确定的话就指定这个类名就好),但是助理必须遵循洗衣协议,即,

    id<WashClothes> delegate

    而我作为整个业务逻辑的代理,我也必须遵循业务逻辑的协议(脑洞再大点)。即,

     User : NSObject <APPBLLDelegate>

    实现文件

    #import "User.h"
    
    @implementation User
    
    - (void)askAssistantForWashingClothes {
        [self.delegate washClothes:self.clothesNum complete:self];
    }
    
    - (void)didWashClothesSuccess:(BOOL)finished leftNum:(int)left {
        if (finished) {
            NSLog(@"助理已洗完衣服,很感谢,支付洗衣费用");
        } else {
            NSLog(@"助理未能洗完衣服,剩余%d件", left);
        }
    }
    
    - (void)didWashClotheFailed:(NSString *)errorMsg {
        if (errorMsg != nil) {
            NSLog(@"报错:%@", errorMsg);
        }
    }
    
    @end

    我不洗衣服,我请求助理洗衣服,那么助理必须遵循洗衣协议,并且实现其具体过程,但我不需要实现,我的任务仅是向代理发送洗衣消息。当我的助理洗完衣服之后,我作为业务逻辑的代理,我必须做出反应,比如我要说谢谢给钱,或者没洗完不给钱,再或者出现问题我要报错。

    这里,业务逻辑可以抽象为一个网络请求,比如,网络状况良好,我请求成功,正确传参,正确返回。或者,网络状况良好,错误传参,错误返回。再或者,网络状况渣渣,就报错吧。。

    4)洗衣助理要做的

    头文件

    #import <Foundation/Foundation.h>
    #import "WashClothes.h"
    
    @interface Assistant : NSObject <WashClothes>
    
    @property int totalNum;
    
    - (instancetype)init;
    
    @end

    洗衣助理必须遵循洗衣协议,即,

    Assistant : NSObject <WashClothes>

    实现洗衣方法,即,

    - (void)washClothes:(int)num complete:(id<APPBLLDelegate>)delegate;

    实现文件

    #import "Assistant.h"
    
    @implementation Assistant
    
    - (instancetype)init {
        self = [super init];
        if (self) {
            self.totalNum = 10;
        }
        return self;
    }
    
    - (void)washClothes:(int)num complete:(id<APPBLLDelegate>)delegate {
    
        int left = self.totalNum - num;
        if (left > 0) {
            [delegate didWashClothesSuccess:YES leftNum:left];
        } else {
            [delegate didWashClothesSuccess:NO leftNum:abs(left)];
            [delegate didWashClotheFailed:@"未能洗完衣服!"];
        }
    }
    
    @end

            洗衣助理在初始化对象时,默认洗衣服最大数目是10件,并实现洗衣服的具体过程,洗完衣服之后给我(业务逻辑的代理)发送消息。也就是说,洗衣助理只需要洗衣服,不需要管洗完衣服做什么,剩下的将结果回传给我,让我来进行消息反馈。

    5)程序单一入口

    #import <Foundation/Foundation.h>
    #import "User.h"
    #import "Assistant.h"
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            
            User *user = [[User alloc] init];
            user.clothesNum = 20;
            
            Assistant *assistance = [[Assistant alloc] init];
            user.delegate = assistance;
            
            [user askAssistantForWashingClothes];
                  
        }
        return 0;
    }

    主程序创建了实例我,设置我的洗衣数量20,创建助理实例,设置我的洗衣委托代理为助理,最后向助理发送消息,助理执行洗衣服这个方法。

    6)运行结果

    2015-07-23 11:34:07.869 NYDelegateTest[1313:68830] 助理未能洗完衣服,剩余10

    2015-07-23 11:34:07.870 NYDelegateTest[1313:68830] 报错:未能洗完衣服!

    5.小结

    1)Objective-C的协议可以实现适配器模式,通过继承和遵循协议,达到C++的多继承效果。通过该机制可以实现多子类的适配,使不同子类拥有各自独特的方法,匹配自身需求。

    2)运用协议代理代码实现时,必须要设置实例的代理,比如,

    user.delegate = assistance;

    这一句必不可少,否则,代理是nil,发送消息就失效,方法不能执行,不会有任何输出。

    3)这个模拟情景中,代码实现了两个协议代理。

    一个是,我找助理作为我的洗衣代理。

    另一个是,助理洗完衣服后,将处理结果回传给我(业务逻辑代理)。

    4)在自学之初,我始终搞不清楚,table view的data source和delegate。现在才明白,其实两者都是协议代理,只不过名字不同罢了。data source的方法主要针对table view的一些UI数据源加载显示。而delegate更多的是对table view的一些操作作出响应(但是也有什么height之类的协议方法,我也是醉了,强迫症伤不起)。总之,如果单独创建table view 而不是用table view controller,data source和delegate都必须设置,且实现其协议方法。

    5)文章如有理解错误,请留言交流,我会及时订正,感谢~

    6)来上海之后,我两个月内成长了很多,学到很多,很感恩。我希望把我所学到明白的东西熟练应用,并能深入理解其原理,所以要学的很多很多。但是现在淡定多了。终生学习,快乐学习。谢谢帮助我的所有人 @不知霜舞哀伤udspj @280me @tinyfool ,我的美女leader @七了个六 。加油加油我可以做的更好!

  • 相关阅读:
    html5 返回当前地理位置的坐标点(经纬度)
    C#趣味程序---百鸡百钱
    Android开发:怎样隐藏自己的app应用
    Android Studio一些简单设置
    集群通信组件tribes之集群的消息接收通道
    Java基础实例
    如何用webbrowser获取ajax动态生成的网页的源码?
    phantomjs 下拉滚动条获取网页的全部源码
    Nodejs+定时截图+发送邮件
    关注网页的更新状况,了解最新的handsup 消息.
  • 原文地址:https://www.cnblogs.com/nycoder/p/4669614.html
Copyright © 2020-2023  润新知