谈谈Objective-C中的协议

协议简述

协议(protocol)是oc中的一个重要的语言特性,协议中定义了一些方法,若某个类想要实现这个协议中的一系列方法,则必须遵守这个协议,这个类对象被称为”委托对象” , 即为”代理”。这也说明这种模式是单向的,消息的发送方(委托方) 需要知道接收方(代理方)是谁,即只需要知道它的代理方是否遵守了协议( protocol)反过来是不需要的。

协议的两种模式

这两种模式分别为委托模式(delegate)和数据源模式(dataSource),区别在于信息的流向不同.

  • 委托模式:信息通过协议中的方法参数从委托方流向代理方,或者事件发生时
    委托者通知代理者 ,简单表述为:委托方传递信息或者事件到代理方。

  • 数据源模式:数据源模式的信息流向与委托模式正好相反,委托方需要从代理方拉取数据。 简单表述为:代理方传递信息到委托方。

    图示 :(箭头表示信息的流向)

《谈谈Objective-C中的协议》 QQ20170720-0@2x.png

协议的作用

  • 进行对象间的相互通信。委托模式时这个委托方可以给代理方回传一些信息,也可以在发生相关事件时通知委托对象.数据源模式时, 代理方可以给委托方回传一些信息,这个信息通常在代理方实现方法的返回值中。
  • 可将数据与业务逻辑解耦。在数据源模式中,委托方只需要构建如何处理这些数据的业务逻辑代码,而不用关心来源,来源由遵守协议的代理方提供。在委托模式中,委托方向代理方提供信息,代理方拿到信息后构建相关的业务逻辑。

协议的声明与使用

为了演示此模式,举一个UIWebView中的UIWebViewDelegate作为例子:

@protocol UIWebViewDelegate <NSObject>

@optional

// 将NSURLRequest对象传递给代理方,代理方由此判断将是否加载此网页,并将结果返回给委托方做处理,这个方法很好的说明了协议用于对象之间的相互通信。
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;
// 通知代理方网页开始加载
- (void)webViewDidStartLoad:(UIWebView *)webView;
// 通知代理方网页加载完成
- (void)webViewDidFinishLoad:(UIWebView *)webView;
// 通知代理方网页加载失败,并将NSError 对象传递给代理方。
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error;

@end
  • 声明协议要用到@protocol 关键字,后面跟着协议名 ,协议如果是委托模式就在类名后面加上Delegate一词,如果是数据源模式就在类名后面加上DataSource一词。整个类名采用‘‘驼峰法’’来写。协议名后面的<>内是这个协议继承的另一个协议,遵守此协议的委托对象也可以实现该协议的方法。协议的多继承也很好的弥补了oc不支持多重继承的语言缺陷。

  • 协议中的方法名要写清楚,方法名应该能准确的传达出委托对象实现该方法的时机和作用。

  • 有了这个协议以后,那么就需要用一个属性来存放委托对象了。这个属性需定义成weak,而不是用strong,如果使用strong,委托方持有委托对象,委托对象也会持有委托方,两个对象之间都是强应用,形成你中有我,我中有你的关系,就会形成循环引用(retain circle) 造成这两个对象无法销毁。

  • 注意协议中的@optional (可选的),声明这个关键字的方法表示委托对象不需要必须实现协议中的所有方法,如UIWebViewDelegate中的webViewDidStartLoad: 协议方法,也许委托对象并不用在网页开始加载时做任何事情,所以可以不必实现这个方法。如果要在委托对象上调用可选方法,那么需要提前判断这个委托对象能否响应相关的选择器,需要使用类型信息查询方法respondsToSelector: 来判断:

    if ([self.delegate respondsToSelector:@selector(webViewDidStartLoad:)]) {
        [self.delegate webViewDidStartLoad:self];
    }

这段代码的意思是来判断委托对象是否实现了相关方法。如果实现了就调用,如果没有实现就不执行任何操作。这样委托对象就可以选择性的实现相关的方法了。即使self.delegate = nil,即没有设置委托对象的时候,程序也能照常运行,因为给nil发送消息将使if语句的值成为false。

  • 在协议方法中,应该总是把委托实例及发起方也一并传入方法中,例如在UIWebViewDelegate协议中,协议方法中总是带有UIWebView实例。这样做的好处是委托对象在实现方法时可以区分不同的委托实例。
- (void)webViewDidStartLoad:(UIWebView *)webView{

    if(webView == self.webViewA){
        // do something;
    } else if (webView == self.webViewB){
       // do something;
    }
}
  • 我将用一个控制器来加载一个webView,如果控制器要实现UIWebViewDelegate的相关协议方法,控制器必须成为webView的委托对象,即代理。

@interface ViewController ()<UIWebViewDelegate>
@end

@implementation ViewController

- (void)viewDidLoad {
    
    [super viewDidLoad];
    
    UIWebView *webView = [[UIWebView alloc] init];
    webView.frame = self.view.bounds;
    [webView loadRequest:request]
    // 成为webView的委托对象,即代理。
    webView.delegate = self;
    [self.view addSubview:webView];
    
}

#pragma mark -- 根据需求选择性的实现协议中的方法

- (void)webViewDidStartLoad:(UIWebView *)webView{
    
}

- (void)webViewDidFinishLoad:(UIWebView *)webView{
    
}

- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error{
    
}

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
    
    return YES;
}

总结

  • 委托模式为某个对象提供了一套接口,这套接口被称为协议,协议中的方法可将相关信息或相关事件告知其他对象,这个对象必须遵守这个协议,并成为这个对象的代理。
  • 协议的作用是进行对象间的相互通信及数据与业务逻辑解耦。

参考资料

*《Effective Objective-C 2.0 编写高质量iOS与OS X代码的52个有效方法》中的第4章 第 23条:通过委托与数据源协议进行对象间通信。

    原文作者:吴与伦
    原文地址: https://www.jianshu.com/p/d7dc2e54cfee
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞