前言
协议是一个重要知识点,类似于其他语言中的接口
一、规范、协议与接口
我们平时生活中会用到U盘,对于我们的主机而言,我们的U盘的插口就需要相同的数据交换方式与相同的实现细节他们是一个类的不同实例
这张图的讲解与我们的u盘插口是类似的。
协议定义了一种规范,他不需要关注是否实现,只规定类中必须提供这些方法。
协议定义了多个类的共同的公共行为规范,这些行为是与外部交流的通道,这就意味着协议里通常是定义一组公用方法,但不会为这些方法提供实现。实现是交给类去处理的
二、使用类别实现非正式协议
当某个类实现NSObject该类别时,就需要实现该类别下的所有方法,这种基于NSObjec定义的类别即可认为是非正式协议。
定义一个Play的类别并定义了一个方法:
我们上面的Play类别中定义了一个taste方法,接下来所有继承NSObject的子类都会自动带有该方法。而且子类可以自行决定是否实现这个方法。
既然类别作为一个非正式协议使用,那么相当于定义了一个规范,因此,遵守该协议的子类通常都会实现这个方法。
然后我们在创建的子类中实现这个方法:
那么这个子类就相当于遵守了我们的协议,那么就可以把我们子类当成Play对象来调用。
最后我们需要指出我们调用该方法时,既可以是非正式协议本身实现该方法,也可以是子类来实现该方法,但是它们之中必须有一个要实现该方法,如果两个都不实现该方法,那么程序就会引起报错。
三、正式协议的定义
对于协议的语法格式的总结如下:
- 一个协议可以有多个直接爸爸协议,但协议只能继承协议,不能继承类,这就像你只能继承你家里人的遗产,不能继承别人家的遗产。
- 协议中定义的方法只有方法签名,不用实现方法。方法既可以是类方法,也可以是实例方法。
协议里的所有方法具有公开访问权限
下面定义几个协议:
如果协议要继承协议,我们一定要记得引入父类协议
最后一个协议同时继承了上述两个协议。
四、遵守(实现)协议
我们现在为printtable提供一个实现类:fkprinter
接口部分:
实现部分:
加入实现类没有完全实现协议中定义的方法,那么会出现如下警告:
现在给出我们的函数部分:
这里我们可以看到我们在后面创建了两个对象,同时通过使他们编译时的类型不同让它们只能调用它们自己协议的方法。
FKPrinter中包含三个协议的实现部分,所以编译部分的方法运行部分一定会有。
我们通过对比正式协议与非正式协议我们可以发现如下的差别:
- 非正式通过创建类别实现,正式则可以直接使用@protocol创建
- 非正式通过继承特定类别的NSObject来实现,正式则有专门的OC语法。
- 书上说非正式不需要实现所有协议中定义的方法,而正式的需要实现。但实际上现在的OC正式协议也不需要实现所有的方法了
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@protocol FKOutput <NSObject>
@optional
- (void)output;//可以选择不实现,编译器不会警告
@required
- (void)addData:(NSString *)msg;//不实现编译器会提示警告
@end
NS_ASSUME_NONNULL_END
五、协议与委托
定义协议的类可以把协议定义的方法委托给实现协议的类,这样可以让类定义具有更好的通用型。
按照书上的例子是说:
当我们在iOS中开发一个表格时会使用到UITableView,我们创建表格时会对表格的功能进行分块处理,例如表格的外形我们会叫给UITableViewDataSourse协议的对象来处理,点击等操作交给UITableViewDelegate协议的对象负责处理
我们可以看到粗体代码就是我们的类方法,他将我们这个部分的事件委托给能够实现这个协议的对象。
这个就像上司定义了一堆方法,然后现在要处理一堆事件,这些事件它自己能够处理,但是它忙不过来,需要将这些事件委托给能处理这些事件的秘书,那这些秘书就必须要实现这些方法。老板把自己的能力分为好几个协议,当有人能够实现某个部分的协议时,就可以将这部分的事件委托给能够实现这些协议的对象。
我们再举一个例子,就是当有人需要给上司打车时,上司就会这些事情委托给秘书处理