Working with Protocols
定义
类的接口用来声明其自身相关的methods和properties;相反,协议用于声明独立于任何特定类的方法和属性。
基本定义语法:声明实例方法、类方法、属性
@protocol ProtocolName
// list of methods and properties
@end
协议可以继承其它协议;NSObject提供了很多常用的协议,因此常见的定义方式
@protocol MyProtocol <NSObject>
// list of methods and properties
@end
用法
下面举例说明用法。例如想要绘制如下图的饼图
为了使得pie chart view的代码能够重复使用,将决定展示内容的代码放到另一个对象中--Datasource。这样可以达到只要更换数据源对象就可以展示不同的内容。
比如说饼图分几部分、各部分占得比例、各部分的标题的协议如下
@protocol XYZPieChartViewDataSource
- (NSUInteger)numberOfSegments;
- (CGFloat)sizeOfSegmentAtIndex:(NSUInteger)segmentIndex;
- (NSString *)titleForSegmentAtIndex:(NSUInteger)segmentIndex;
@end
Datasource
协议定义好了,此view还需要一个property来记录datasource对象,这个对象可能是任何类的实例,因此声明为id类型;对于view来说只知道此对象遵守XYZPieChartViewDataSource协议。Objective-C使用尖括号来表示遵守此协议。
@interface XYZPieChartView : UIView
@property (weak) id <XYZPieChartViewDataSource> dataSource;
...
@end
注意:
1、此处的weak来修饰datasource是为了避免循环引用。
2、datasource对象需要声明遵守协议,否则编译器发布警告
3、对于view来说datasource的类型不重要,重要是遵守并实现了协议
必选、可选协议
1、通过编译器指令@optional指定跟在后边的方法均为可选;
2、通过编译器指令@required指定跟在后边的方法均为必选;
3、不声明则默认必选;
例如
@protocol XYZPieChartViewDataSource
- (NSUInteger)numberOfSegments;
- (CGFloat)sizeOfSegmentAtIndex:(NSUInteger)segmentIndex;
@optional
- (NSString *)titleForSegmentAtIndex:(NSUInteger)segmentIndex;
- (BOOL)shouldExplodeSegmentAtIndex:(NSUInteger)segmentIndex;
@required
- (UIColor *)colorForSegmentAtIndex:(NSUInteger)segmentIndex;
@end
表示声明了3个必选方法和2个可选方法。
对于可选方法,要在runtime期间通过respondsToSelector:
方法检测对象是否已实现;例如
//局部对象变量自动初始化为nil
NSString *thisSegmentTitle;
if ([self.dataSource respondsToSelector:@selector(titleForSegmentAtIndex:)]) {
thisSegmentTitle = [self.dataSource titleForSegmentAtIndex:index];
}
协议继承
像类继承一样,协议也可以继承。例如最好使得自定义协议继承NSObject协议(NSObject接口分离出来组成的独立的协议)。由于MyProtocol继承NSObject protocol,所有凡是遵守MyProtocol的对象都将实现NSObject protocol中声明的方法,然后你并不担心这些方法的实现,因为使用的绝大多数类都继承NSObject,已经默认实现过了。协议定义变成这样
@protocol MyProtocol <NSObject>
// list of methods and properties
@end
In this example, any object that adopts MyProtocol also effectively adopts all the methods declared in the NSObject protocol.
遵守协议
通过尖括号来声明一个类遵守协议,例如
@interface MyClass : NSObject <MyProtocol, AnotherProtocol, YetAnotherProtocol>
...
@end
提示:
1、如果一个类遵守的协议过多,不便于维护,建议分割成独立的类去实现。
2、如果未实现协议要求必须实现的方法,编译器将发出警告。
3、实现协议方法时,方法名称、参数类型必须和协议声明的保持一致。
协议屏蔽类的实现
协议除了让不同的类按照指定的方式完成一定的任务之外,还将功能抽离出来提高代码复用率。协议的标识的是相互协作的类之间没有层次关系,这样使得不相干的类可以使用相同的功能。比如说NSArray和NSDictionary都遵守NSCoding协议,使得其本身能方便的进行本地存储;遵守NSFastEnumeration协议,使得能够快速枚举元素。
协议除了上述作用之外,还能屏蔽类的实现。当不知道某个对象的所属类时即id或者需要隐藏的时候,协议可以起到很好的作用。例如,开发者框架中可能没有发布类的接口声明,由于class未知,不能够直接创建实例对象
id utility = [frameworkObject anonymousUtility];
为了anonymousUtility对象能够使用,SDK发不了一个协议来暴露其部分方法,尽管对class一无所知,但是还能勉强能够使用了
id <XYZFrameworkUtility> utility = [frameworkObject anonymousUtility];
此时utility变量可以使用XYZFrameworkUtility中声明的方法。
参考文献:Working with Protocols
Runtime--Protocols
查询协议相关信息
首先先来声明一个协议DataDelegate,ViewController类遵守,然后结合例子说明接口
@protocol DataDelegate <NSObject>
@property(nonatomic,copy)NSString *name;
-(void)ivarMethod;
@end
1、获取所有runtime时的协议
/* 获取所有协议名称 */
unsigned int protocol_count = 0;
__unsafe_unretained Protocol **protocol_list = objc_copyProtocolList(&protocol_count);
for (int i = 0; i < protocol_count ; i ++)
{
Protocol *protocol = protocol_list[i];
const char *name_class = protocol_getName(protocol);//名称
NSLog(@"index:%d name_class:%@",i,[NSString stringWithUTF8String:name_class]);
}
free(protocol_list);
//输出结果:过多仅做部分展示
index:0 name_class:SBSRemoteAlertClientHandle
index:1 name_class:_UIContentContainerInternal
index:2 name_class:NSFetchRequestResult
index:3 name_class:CAMLWriterDelegate
index:4 name_class:CABehaviorDelegate
index:5 name_class:GEOTransitSystem
index:6 name_class:GEOMapItemPhoto
index:7 name_class:UIInputViewAnimationHost
index:8 name_class:UIAdaptivePresentationControllerDelegate
index:9 name_class:_UISharingPublicController
index:10 name_class:NSISEngineDelegate
index:11 name_class:UIPopoverPresentationControllerDelegate
2、根据名称获取协议
/* 获取协议 */
Protocol *protocol = objc_getProtocol("DataDelegate");
3、获取名称
/* 获取名称 */
const char *pro_name_char = protocol_getName(protocol);
NSString *protocolName = [NSString stringWithUTF8String:pro_name_char];
NSLog(@"protocolName:%@",protocolName);
//输出结果
protocolName:DataDelegate
4、获取方法描述
/* 获取方法描述 */
struct objc_method_description protocol_method_description = protocol_getMethodDescription(protocol, @selector(ivarMethod), YES, YES);
NSLog(@"name:%@ type:%@",NSStringFromSelector(protocol_method_description.name),[NSString stringWithUTF8String:protocol_method_description.types]);
//输出结果:空返回、无参数
name:ivarMethod type:v16@0:8
/* 获取方法列表描述 */
unsigned int methodCount = 0;
struct objc_method_description *method_description_list = protocol_copyMethodDescriptionList(protocol, YES, YES, &methodCount);
for (int i = 0; i < methodCount ; i ++)
{
struct objc_method_description description = method_description_list[i];
NSLog(@"index:%d name:%@ type:%@",i,NSStringFromSelector(description.name),[NSString stringWithUTF8String:description.types]);
}
free(method_description_list);
//输出结果
index:0 name:ivarMethod type:v16@0:8
index:1 name:name type:@16@0:8
index:2 name:setName: type:v24@0:8@16
5、获取property
/* 获取property */
objc_property_t property = protocol_getProperty(protocol, "name", YES, YES);
const char *propeytyName = property_getName(property);
NSLog(@"propeytyName:%@",[NSString stringWithUTF8String:propeytyName]);
//输出结果
propeytyName:name
/* 获取property列表 */
unsigned int propertyCount = 0;
objc_property_t *property_list = protocol_copyPropertyList(protocol, &propertyCount);
for (int i = 0; i < propertyCount ; i ++)
{
objc_property_t property = property_list[i];
const char *propeytyName = property_getName(property);
NSLog(@"index:%d propeytyName:%@",i,[NSString stringWithUTF8String:propeytyName]);
}
free(property_list);
//输出结果
index:0 propeytyName:name
6、获取继承的协议列表
/* 获取继承的协议 */
unsigned int protocol_count = 0;
__unsafe_unretained Protocol **protocol_list = protocol_copyProtocolList(protocol, &protocol_count);
for (int i = 0; i < protocol_count ; i ++)
{
Protocol *protocol = protocol_list[i];
const char *name_class = protocol_getName(protocol);//名称
NSLog(@"index:%d name_class:%@",i,[NSString stringWithUTF8String:name_class]);
}
free(protocol_list);
//输出结果
index:0 name_class:NSObject
7、判断两个协议是否相等
/* 是否相等*/
BOOL isEqual = protocol_isEqual(protocol, @protocol(DataDelegate));
NSLog(@"是否相等:%d",isEqual);
//输出结果
是否相等:1
8、判断协议A是否遵守(继承)协议B
/* 是否遵守 */
BOOL isconform = protocol_conformsToProtocol(protocol, @protocol(NSObject));
NSLog(@"是否遵守:%d",isconform);
//输出结果
是否遵守:1
创建新的协议
OBJC_EXPORT Protocol *objc_allocateProtocol(const char *name)
OBJC_AVAILABLE(10.7, 4.3, 9.0, 1.0);
OBJC_EXPORT void objc_registerProtocol(Protocol *proto)
OBJC_AVAILABLE(10.7, 4.3, 9.0, 1.0);
说明
1、新的协议只有被register才能使用
2、新的协议可以添加Method、Property、遵守的Protocol
3、所有的添加操作都必须在objc_allocateProtocol和objc_registerProtocol直接进行,也就是说在协议的构建期间进行
在上述代码的基础上
/* 创建新的协议 */
Protocol *newProtocol = objc_allocateProtocol("DataDelegate2");
/* 添加property */
objc_property_attribute_t attr_T = {"T","@\"NSString\""};//@encod
objc_property_attribute_t attr_N = {"N",""};//原子性
objc_property_attribute_t attr_C = {"C",""};//copy
objc_property_attribute_t attr_V = {"V","_age"};//实例变量
objc_property_attribute_t attrs[] = {attr_T,attr_N,attr_C,attr_V};
protocol_addProperty(newProtocol, "age", attrs, 4, YES, YES);
/* 添加遵守的协议 */
protocol_addProtocol(newProtocol, @protocol(NSObject));
/* 添加方法方法 */
protocol_addMethodDescription(newProtocol, @selector(ivarMethod2), "v@:", YES, YES);
/* 注册新协议 */
objc_registerProtocol(newProtocol);
//获取property列表
unsigned int propertyCount2 = 0;
objc_property_t *property_list2 = protocol_copyPropertyList(newProtocol, &propertyCount2);
for (int i = 0; i < propertyCount2 ; i ++)
{
objc_property_t property = property_list2[i];
const char *propeytyName = property_getName(property);
NSLog(@"2 index:%d propeytyName:%@",i,[NSString stringWithUTF8String:propeytyName]);
}
free(property_list2);
//获取方法列表
unsigned int methodCount2 = 0;
struct objc_method_description *method_description_list2 = protocol_copyMethodDescriptionList(newProtocol, YES, YES, &methodCount2);
for (int i = 0; i < methodCount2 ; i ++)
{
struct objc_method_description description = method_description_list2[i];
NSLog(@"2 index:%d name:%@ type:%@",i,NSStringFromSelector(description.name),[NSString stringWithUTF8String:description.types]);
}
free(method_description_list2);
//获取新协议遵守的协议
unsigned int protocol_count2 = 0;
__unsafe_unretained Protocol **protocol_list2 = protocol_copyProtocolList(newProtocol, &protocol_count2);
for (int i = 0; i < protocol_count2 ; i ++)
{
Protocol *protocol = protocol_list2[i];
const char *name_class = protocol_getName(protocol);//名称
NSLog(@"2 index:%d name_class:%@",i,[NSString stringWithUTF8String:name_class]);
}
free(protocol_list2);
//输出结果
2 index:0 propeytyName:age//property
2 index:0 name:ivarMethod2 type:v@://Method
2 index:0 name_class:NSObject//protocol
协议添加完成后是需要指定类使用的
/* 检测类是否遵守指定的协议 */
BOOL conform = class_conformsToProtocol([ViewController class], protocol);
NSLog(@"conform:%d",conform);
/* 添加新的协议 */
class_addProtocol([ViewController class], newProtocol);
/* 获取类遵守的协议列表 */
unsigned int classProtocolCount = 0;
__unsafe_unretained Protocol **classProtocolList = class_copyProtocolList([ViewController class], &classProtocolCount);
for (int i = 0; i < classProtocolCount ; i ++)
{
Protocol *protocol = classProtocolList[i];
const char *name_class = protocol_getName(protocol);//名称
NSLog(@"class index:%d name_class:%@",i,[NSString stringWithUTF8String:name_class]);
}
free(classProtocolList);
//输出结果
conform:1 //遵守
class index:0 name_class:DataDelegate2 //添加完成
class index:1 name_class:DataDelegate //本来就遵