总述
运行时(runtime)是一种面向对象的编程语言的运行环境.
运行时(runtime)是Objective-C的核心, Objective-C就是基于运行时(runtime)的.
Objective-C是基于C语言加入了面向对象特性和消息转发机制的动态语言.
Objective-C需要一个编译器,还需要Runtime系统来动态创建类和对象,进行消息发送和转发。
Objective-C最主要的特点就是在程序运行时, 以发送消息的方式调用方法.
C语言的函数调用方式是使用静态绑定(static binding).在编译期就能决定运行时所应调用的函数.
在Objective-C中,如果向某对象传递消息,那就会使用动态绑定机制来决定需要调用的方法。在底层,所有方法都是普通的C语言函数。
头文件
import <objc/runtime.h>
import <objc/message.h>
OC方法调用
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[self testDemo:nil];
}
- (void)testDemo:(id)param {
NSLog(@"%s",__func__);
}
分析[self testDemo];
1.self 叫做 接收者(receiver)
2.testDemo 叫做 选择子(selector)
3.选择子与参数合起来称为 消息(message)
4.编译器看到此消息后,将其转换为一条标准的C语言函数调用
5.所调用的函数是消息传递机制中的核心函数,叫做objc_msgSend()
运行时(runtime)消息发送 == OC方法调用底层实现
运行时(runtime)消息发送函数:OBJC_EXPORT id objc_msgSend(id self, SEL op, ...)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
OBJC2_UNAVAILABLE是一个Apple对Objc系统运行版本进行约束的宏定义,主要为了兼容非Objective-C 2.0的遗留版本
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
// 消息发送 : iOS8以后的特殊写法
((void(*)(id,SEL,id))objc_msgSend)(self,@selector(testDemo:),nil);
}
- (void)testDemo:(id)param {
NSLog(@"%s",__func__);
}
数据类型分析
SEL : 方法选择器(指向objc_selector结构体的指针)
typedef struct objc_selector *SEL;
1.可以通过Objc编译器命令@selector()或者Runtime系统的sel_registerName()函数来获取一个SEL类型的方法选择器.
2.如果知道selector对应的方法名是什么,可以通过NSString* NSStringFromSelector(SEL aSelector)方法将SEL转化为OC字符串
id : 对象(指向objc_object结构体的指针)
// objc_object结构体
struct objc_object {
// id的成员 : isa
Class isa OBJC_ISA_AVAILABILITY;
};
// 指向objc_object结构体指针
typedef struct objc_object *id;
1.包含一个Class isa成员.
2.根据isa指针就可以找到对象所属的类.
Class : 对象所属的类(指向objc_class结构体的指针)<if见面自动加井号>
typedef struct objc_class *Class;
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;
if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE;
const char *name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE;
struct objc_method_list **methodLists OBJC2_UNAVAILABLE;
struct objc_cache *cache OBJC2_UNAVAILABLE;
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE;
endif
} OBJC2_UNAVAILABLE;
Method : 方法(指向objc_method结构体的指针)
typedef struct objc_method *Method;
struct objc_method {
SEL method_name OBJC2_UNAVAILABLE;
char *method_types OBJC2_UNAVAILABLE;
IMP method_imp OBJC2_UNAVAILABLE;
}
objc_method存储了方法名(method_name)、方法类型(method_types)和方法实现(method_imp)等信息.
method_imp的数据类型是IMP,它是一个函数指针.
IMP : 方法实现(指向方法实现的指针)
#if !OBJC_OLD_DISPATCH_PROTOTYPES
typedef void (*IMP)(void /* id, SEL, ... */ );
#else
typedef id (*IMP)(id, SEL, ...);
#endif
IMP本质上就是一个函数指针,指向方法的实现.
当向某个对象发送一条信息时,可以由这个函数指针来指定方法的实现,它最终就会执行那段代码.
Ivar : 实例变量(指向objc_ivar结构体的指针)
typedef struct objc_ivar *Ivar;
struct objc_ivar {
char *ivar_name OBJC2_UNAVAILABLE;
char *ivar_type OBJC2_UNAVAILABLE;
int ivar_offset OBJC2_UNAVAILABLE;
#ifdef __LP64__
int space OBJC2_UNAVAILABLE;
#endif
}
Cache : 缓存(指向结构体objc_cache的指针)
typedef struct objc_cache *Cache OBJC2_UNAVAILABLE;
struct objc_cache {
unsigned int mask /* total = mask + 1 */ OBJC2_UNAVAILABLE;
unsigned int occupied OBJC2_UNAVAILABLE;
Method buckets[1] OBJC2_UNAVAILABLE;
}
其实就是一个存储Method的链表,主要是为了优化方法调用的性能.
类关系图
1.实例对象在运行时被表示成 objc_object 类型结构体,结构体内部有个isa指针指向 objc_class 结构体。
2.objc_class 内部保存了类的变量和方法列表以及其他一些信息,并且还有一个isa指针。这个isa指针会指向 metaClass(元类),元类里保存了这个类的类方法列表。
3.元类里也有一个isa指针,这个isa指针,指向的是根元类,根元类的isa指针指向自己
objc_class中信息查看
Class : 对象所属的类(指向objc_class结构体的指针)
typedef struct objc_class *Class;
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE;
const char *name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE; // 位标识
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE;
struct objc_method_list **methodLists OBJC2_UNAVAILABLE;
struct objc_cache *cache OBJC2_UNAVAILABLE;
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
代码示例
获取类名
/**
获取类名
@param cls 要获取类名的类
@return 类名
*/
+ (NSString *)getClassName:(Class)cls {
// 获取类名(C语言类型)
const char *cName = class_getName(cls);
// OC类型类名
NSString *className = [NSString stringWithUTF8String:cName];
return className;
}
获取成员变量列表
/**
获取成员变量列表(带下划线的都获取)
@param cls 要获取成员变量列表的类
@return 成员变量数组(成员变量名字和类型组合的字典数组)
*/
+ (NSArray *)getIvarList:(Class)cls {
// 成员变量个数
unsigned int count;
// 获取所有的成员变量
Ivar *ivarList = class_copyIvarList(cls, &count);
// 准备数组容器
NSMutableArray *ivarArrM = [NSMutableArray arrayWithCapacity:count];
// 遍历成员变量
for (NSInteger i = 0; i < count; i++) {
// 获取成员变量名字
const char *ivarName = ivar_getName(ivarList[i]);
// 获取成员变量类型
const char *ivarType = ivar_getTypeEncoding(ivarList[i]);
// 成员变量名字和类型组合的字典容器
NSMutableDictionary *ivarDictM = [NSMutableDictionary dictionary];
ivarDictM[@"name"] = [NSString stringWithUTF8String:ivarName];
ivarDictM[@"type"] = [NSString stringWithUTF8String:ivarType];
// 添加到数组容器
[ivarArrM addObject:ivarDictM];
}
free(ivarList);
return ivarArrM.copy;
}
获取属性列表
/**
获取属性列表(有setter和getter方法的属性)
@param cls 要获取属性列表的类
@return 属性数组(属性名字和属性描述的字典数组)
*/
+ (NSArray *)getPropertyList:(Class)cls {
// 成员属性个数
unsigned int count;
// 获取所有成员变量
objc_property_t *propertyList = class_copyPropertyList(cls, &count);
// 成员属性容器
NSMutableArray *propertyArrM = [NSMutableArray arrayWithCapacity:count];
// 遍历成员属性
for (NSInteger i = 0; i < count; i++) {
// 获取属性名字和属性的属性描述
NSString *name = [NSString stringWithUTF8String:property_getName(property)];
NSString *attr = [NSString stringWithUTF8String:property_getAttributes(property)];
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
dict[@"name"] = name;
dict[@"attr"] = attr;
// 添加到数组容器
[propertyArrM addObject:[dict copy]];
}
free(propertyList);
return propertyArrM.copy;
}
类属性中的属性示例
@property (nonatomic,strong, setter=setPublicProperty:) NSArray *publicProperty01;
property_getAttributes输出为:
attr = "T@\"NSArray\",&,N,SsetPublicProperty:,V_publicProperty01"; name = publicProperty01;
其中 attr 的解释为:
- T 代表类型标识
- @ 代表为对象类型
- NSArray 表示其实际类型
- & 代表 retain 强引用(copy 用 C, weak 用 W)
- N 代表 nonatomic (代表 natomic)
- SsetPublicProperty: 前面大写S代表指定了 setter,后面跟着代表具体方法
- V_publicProperty01 V代表其对应的成员,后面为成员的名字
获取方法列表
/**
获取类的实例方法 : 属性的setter和getter方法,对象方法,不包括类方法
@param cls 要获取类的实例方法的类
@return 方法数组
*/
+ (NSArray *)getMethodList:(Class)cls {
// 实例方法个数
unsigned int count;
// 获取所有方法(不包括类方法)
Method *methodList = class_copyMethodList(cls, &count);
// 方法容器
NSMutableArray *methodArrM = [NSMutableArray arrayWithCapacity:count];
// 遍历所有方法
for (NSInteger i = 0; i < count; i++) {
// 获取数据
NSString *name = [NSString stringWithString:NSStringFromSelector(method_getName(method))];
NSString *type = [NSString stringWithUTF8String:method_getTypeEncoding(method)];
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
dict[@"name"] = name;
dict[@"type"] = type;
// 添加到数组
[methodArrM addObject:[dict copy]];
}
// 通过元类获取类方法
Class metaCls = objc_getMetaClass(class_getName(cls));
methods = class_copyMethodList(metaCls, &count);
for (NSInteger i = 0; i < count; i++) {
Method method = methods[i];
// 获取数据
NSString *name = [NSString stringWithString:NSStringFromSelector(method_getName(method))];
NSString *type = [NSString stringWithUTF8String:method_getTypeEncoding(method)];
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
dict[@"name"] = name;
dict[@"type"] = type;
// 添加到数组
[arrayM addObject:[dict copy]];
}
free(methodList);
return methodArrM.copy;
}
方法类型编码示例
/// 公有方法2
- (void)publicMethodd02:(NSString *)str append:(int)age;
method_getTypeEncoding 的输出为:
name = "publicMethodd02:append:"; type = "v28@0:8@16i24";
其中type解释为:
- v 代表返回值为void
- 28 代表整个方法参数占位的总长度
- @0 @代表对象,objc_msgSend 函数传入的第1个参数(self), 后面的0代表位置0开始
- :8 :代表SEL,objc_msgSend 函数传入的第2个参数(self),后面的8代表位置8开始
- @16 @代表第1个参数的类型为对象类型,后面的16代表位置8开始
- i24 i代表第2个参数的类型为int类型,后面的24代表位置24开始
获取协议列表
/**
获取类的协议列表
@param cls 获取协议列表的类
@return 协议数组
*/
+ (NSArray *)getProtocolList:(Class)cls {
// 协议个数
unsigned int count;
// 获取协议列表
Protocol * __unsafe_unretained *protocolList = class_copyProtocolList(cls, &count);
// 协议容器
NSMutableArray *protocolArrM = [NSMutableArray arrayWithCapacity:count];
// 遍历协议列表
for (NSInteger i = 0; i < count; i++) {
// 获取协议名字
const char *protocolName = protocol_getName(protocolList[i]);
// 添加到协议容器
[protocolArrM addObject:[NSString stringWithUTF8String:protocolName]];
}
free(protocolList);
return protocolArrM.copy;
}