Runtime--Protocols

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 //本来就遵

参考文献:Objective-C RuntimeType Encodings

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值