上一章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结尾,虽然无强制要求,但是最好有一个好的约定。
对于协议的应用场景,特别说一下,虽然协议相当于接口,但是不能使用协议的指针指向实例对象,所以它不能用于多态,不能用于多态!
协议的一个常见的应用场景就是提供给程序员自定义功能实现,也就是在已经实现了的一个功能系统A里面,额外地使用一个协议,当使用A的时候,程序员就可以自定义实现这个协议,达到自定义功能的目的。例如UIKit中的UITableViewDelegate和UITableViewDataSource,其它如实现函数回调也可以使用协议。
评论前必须登录!
注册