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 @七了个六 。加油加油我可以做的更好!