IOS Object-C 中Runtime详解及实例代码

2020-01-18 21:31:31王冬梅

IOS Object-C 中Runtime详解

最近了解了一下OC的Runtime,真的是OC中很强大的一个机制,看起来比较底层,但其实可以有很多活用的方式。

什么是Runtime

我们虽然是用Objective-C写的代码,其实在运行过程中都会被转化成C代码去执行。比如说OC的方法调用都会转成C函数 id objc_msgSend ( id self, SEL op, … ); 而OC中的对象其实在Runtime中都会用结构体来表示,这个结构体中包含了类名、成员变量列表、方法列表、协议列表、缓存等。

类在Runtime中的表示:


struct objc_class {
 Class isa;//指针,顾名思义,表示是一个什么,
 //实例的isa指向类对象,类对象的isa指向元类

#if !__OBJC2__
 Class super_class; //指向父类
 const char *name; //类名
 long version;
 long info;
 long instance_size
 struct objc_ivar_list *ivars //成员变量列表
 struct objc_method_list **methodLists; //方法列表
 struct objc_cache *cache;//缓存
 //一种优化,调用过的方法存入缓存列表,下次调用先找缓存
 struct objc_protocol_list *protocols //协议列表
 #endif
} OBJC2_UNAVAILABLE;
/* Use `Class` instead of `struct objc_class *` */

整个Runtime机制其实可以挖的点很多,这里只是简单的介绍一些常见的用法,如果将其细细解析,相信一定会对OC的理解加深几个层面。

获取属性/方法/协议列表

最直接的一种用法,就是获取我们的结构体中存储的对象的属性、方法、协议等列表,从而获取其所有这些信息。

要获取也比较简单,但是自己尝试之前需要注意几点:

一定要自己给类加几个属性、方法,遵循一些协议,否则当然是看不到输出信息的。

要使用这些获取的方法,需要导入头文件 #import


#import <objc/runtime.h>

// 输出类的一些信息
- (void)logInfo {
 unsigned int count;// 用于记录列表内的数量,进行循环输出

 // 获取属性列表
 objc_property_t *propertyList = class_copyPropertyList([self class], &count);
 for (unsigned int i = 0; i < count; i++) {
 const char *propertyName = property_getName(propertyList[i]);
 NSLog(@"property --> %@", [NSString stringWithUTF8String:propertyName]);
 }

 // 获取方法列表
 Method *methodList = class_copyMethodList([self class], &count);
 for (unsigned int i; i < count; i++) {
 Method method = methodList[i];
 NSLog(@"method --> %@", NSStringFromSelector(method_getName(method)));
 }

 // 获取成员变量列表
 Ivar *ivarList = class_copyIvarList([self class], &count);
 for (unsigned int i; i < count; i++) {
 Ivar myIvar = ivarList[i];
 const char *ivarName = ivar_getName(myIvar);
 NSLog(@"Ivar --> %@", [NSString stringWithUTF8String:ivarName]);
 }

 // 获取协议列表
 __unsafe_unretained Protocol **protocolList = class_copyProtocolList([self class], &count);
 for (unsigned int i; i < count; i++) {
 Protocol *myProtocal = protocolList[i];
 const char *protocolName = protocol_getName(myProtocal);
 NSLog(@"protocol --> %@", [NSString stringWithUTF8String:protocolName]);
 }
}