Objective-C协议(Protocol)用法解析 – Objective-C开发教程

上一章Objective-C开发教程请查看:Objective-C扩展(Extension)用法解析?
这一章我们讨论Objective-C中的协议,协议是个什么东西呢?在说协议之前我们先总结一下之前讨论的继承、类别和扩展:

  • OC中的继承和一般OOP中的继承类似,子类继承父类的属性和方法,属于对类的纵向扩展,但是同时又具备多态功用,也就是也支持横向扩展。
  • 类别也类似于继承,但是类别更像是向原有类嵌入一系列功能,目标类有没有源码都可以使用。
  • 扩展是一个匿名类名,既然是匿名,那就是即时用的了,用完即止。只能在所用的源文件中使用,在其它地方它不起作用。
类别可用于当API接口不可控的时候,对一些类进行扩展;扩展可用于为类设计私有方法的时候,针对的是自定义类,原有定义类和扩展声明的方法,都是一同在implementation中实现的(参考iOS项目默认生成的controller),特别要注意扩展中增加的任何方法和属性在外部都是不能用的(但是可以通过公有方法使用或调用)。
那么和继承、类别和扩展相比,协议是个什么东西呢?其实也是一个类,但是就像上面说明的,继承提供横向扩展(一般来说横向扩展由接口提供),但是使用类继承进行横向扩展是比较麻烦的。协议就相当于Java中的接口了,其功能和类多态类似,但是会更加简洁。
为什么称为协议呢?通俗的说就是约定,更符合编程的说就是协议,比如HTTP协议,规定web数据的传输规范,这个协议是官方标准规定的,具体内容由提供商实现。或者直接理解为接口也是没问题的。
协议的定义协议的定义和类和类别的定义类似,以关键字@protocol开始,以@end结束,中间是方法声明。方法声明有两种:@required和@optional,前者表示实现者必须实现的方法,后者表示实现是可选的,可实现可不实现,如果不使用这两个关键字,则默认为@required必须实现的。
协议的定义语法如下:
@protocol ProtocolName < OtherProtocol1, OtherProtocol2> @required // 必须实现的属性/方法列表 @optional // 可选实现的属性/方法列表 @end

注意,协议中声明一般都是方法,但是也可以声明属性。推荐的写法是:必须实现的方法或必须使用的属性可以不用@required说明,只有遇到可选的属性和方法时再使用@optional。
协议也可以继承另一个协议,另外继承的协议在协议名后面的尖括号中添加,多个协议使用逗号隔开。
下面是声明协议的一个具体例子:
#import < Foundation/Foundation.h>@protocol MyProtocol// 必须实现的属性和方法 @property(nonatomic, copy)NSString *name; -(void)run; // 必须实现 @required @property(nonatomic, assign)int size; -(void)print; // 可选实现 @optional @property(nonatomic, copy)NSArray *desc; -(void)printDesc; @end

实现协议实现协议在OC中又称为遵循协议,在iOS开发中实现者又称为代理Delegate。实现协议就是新建一个类,让这个类实现指定的协议,协议的名称在尖括号后面添加,多个协议使用逗号隔开,但是别忘了新建的类还是要继承一个类的。
实现协议的语法如下:
@interface myClass< myProtocol> @interface myClass :NSObject< myProtocol> @interface myClass :NSObject< myProtocol, NSCoding>

其它就是在@implementation中实现即可,建议还是按照标准的方式,该实现还是实现,可选实现留空也没问题,不要搞的有的没的。
下面是实现协议的具体例子:
#import < Foundation/Foundation.h> #import "MyProtocol.h"@interface MyProtoclImp : NSObject < MyProtocol>@end

以下是详细的实现细节,下面的方式不是标准的编程写法,仅供示范:
// //MyProtoclImp.m //basic // //Created by cococ on 2020/1/4. //Copyright ? 2020年 cococ. All rights reserved. //#import "MyProtoclImp.h"@implementation MyProtoclImp @synthesize name = _name; @synthesize size = _size; @synthesize desc = _desc; -(void)run{ _name = @"Running"; NSLog(@"name: %@", _name); }-(void)print{ _size = 13; NSLog(@"size: %d", _size); }-(void)printDesc{ NSArray *array = [[NSArray alloc]initWithObjects:@5, @2, @8, nil]; _desc = array; for (id obj in array) { NSLog(@"%@", obj); } }@end

要注意的是,在协议中声明的属性,一般在实现文件中下划线属性是不可用的,不会自动合成属性下划线,要有下划线属性,需要手动使用@synthesize指定合成的属性,否则不手动指定也是可以的,直接使用无下划线的属性即可。
协议的使用和其它OOP语言不同,OC不能使用协议的指针指向实现者的实例,有两种方式可以定义一个实现了协议的对象:一种和普通的类对象创建一样,如下:
MyProtocolImp< MyProtocol> *imp = [MyProtocolImp new];

尖括号中是实现的协议名称,表示创建的对象是实现了该协议的,而且按照规范只能调用协议中声明的方法和属性,不指定协议名称也是可以的,但是还是建议指定为好。
另一种是使用万能指针id,务必注意id是一个指针,相当于C中的void*,这个在iOS开发中使用代理Delegate尤为常见。那么使用id指针也就变成了以下的形式:
id< MyProtocol> *imp = [MyProtocolImp new];

下面是协议的测试用例:
void testProtocol(void){ // MyProtoclImp< MyProtocol> *imp = [MyProtoclImp new]; id< MyProtocol> imp = [MyProtoclImp new]; [imp run]; [imp print]; [imp printDesc]; }int main(int argc, const char * argv[]) { testProtocol(); return 0; }

协议小结协议相当于接口,其中可以声明属性和方法,但是要注意下滑属性需要手动使用@synthesize关键字指定。协议中的属性和方法分为@required和@optional两种,不使用这两个关键字说明的默认为@required。
在使用协议的建议使用尖括号< >指定类所实现的协议,这样可以让代码更为清晰。在Java中类似协议的称为接口,所以一般接口名称后面为Interface,实现类后面为Imp。在OC开发中,协议的名称可以以Protocol结尾,实现类以Delegate结尾,虽然无强制要求,但是最好有一个好的约定。
对于协议的应用场景,特别说一下,虽然协议相当于接口,但是不能使用协议的指针指向实例对象,所以它不能用于多态,不能用于多态!
【Objective-C协议(Protocol)用法解析 – Objective-C开发教程】协议的一个常见的应用场景就是提供给程序员自定义功能实现,也就是在已经实现了的一个功能系统A里面,额外地使用一个协议,当使用A的时候,程序员就可以自定义实现这个协议,达到自定义功能的目的。例如UIKit中的UITableViewDelegate和UITableViewDataSource,其它如实现函数回调也可以使用协议。

    推荐阅读