一、runtime简介
Objective-C是一门动态语言,它的一切都是基于runtime实现的。对于Objective-C来说,这个运行时系统就像一个操作系统一样,让所有的工作都能正常运行,在程序运行中,Objective-C最终都转换成了runtime的C语言代码。runtime是一套底层的纯C语言的API,属于1个C语言库,包含了很多的C语言API。runtimeAPI的实现是用C++开发的,是一套苹果开源的框架。
在runtime中,对象可以用C语言中的结构体表示,而方法可以用C语言实现,另外加上一些额外特性,这些结构体和函数被runtime函数封装后,让Objective-C可以面向对象编程。
二、runtime消息传递
Objective-C中的方法调用并不是直接进行调用方法,而是转换为消息传递。其大致过程如下:
- 使用
[receiver message]
语法时,编译器会转成消息发送objc_msgSend(receiver,message)
- receiver会通过
isa
指针找到其所属的类,然后在cache
或者methodLists
中查找message
(类方法查找元类),找到就去执行它的实现IMP
- 如果在类中没有找到该方法,则通过
super_class
往上一级父类查找(如果一直找到NSObject都没有,就进行消息转发)
objc_msgSend的方法定义如下:
OBJC_EXPORT id _Nullable
objc_msgSend(id _Nullable self, SEL _Nonnull op, ...)
该函数有两个参数,一个 id 类型,一个 SEL 类型
id(obj_object)
/// Represents an instance of a class.
struct objc_object {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};
/// A pointer to an instance of a class.
typedef struct objc_object *id;
我们通常说的对象就是这个样子,这个对象被定义在<objc/objc.h>中,其只有一个isa成员变量,对象可以通过isa指针找到其所属的类
SEL(objc_selector)
/// An opaque type that represents a method selector.
typedef struct objc_selector *SEL;
SEL也被定义在<objc/objc.h>中,它是个映射到方法的C字符串,你可以用 Objective-C 编译器命令 @selector() 或者 runtime 系统的 sel_registerName 函数来获得一个 SEL 类型的方法选择器。
IMP
/// A pointer to the function of a method implementation. 指向一个方法实现的指针
#if !OBJC_OLD_DISPATCH_PROTOTYPES
typedef void (*IMP)(void /* id, SEL, ... */ );
#else
typedef id _Nullable (*IMP)(id _Nonnull, SEL _Nonnull, ...);
#endif
IMP也被定义在<objc/objc.h>中,IMP本质上就是一个函数指针,指向方法的实现,当你向某个对象发送一条信息,可以由这个函数指针来指定方法的实现,它最终就会执行那段代码,这样可以绕开消息传递阶段而去执行另一个方法实现。
三、runtime中用到的概念
<objc/runtime.h>中对一个类进行了完全的分解,将类定义的每一个部分都抽象为一个type,用结构体来表示。
/// An opaque type that represents a method in a class definition.描述类中的一个方法
typedef struct objc_method *Method;
/// An opaque type that represents an instance variable.实例变量
typedef struct objc_ivar *Ivar;
/// An opaque type that represents a category.类别Category
typedef struct objc_category *Category;
/// An opaque type that represents an Objective-C declared property.类中声明的属性
typedef struct objc_property *objc_property_t;
/// 类在runtime中的表示,实例的isa指向类对象,类对象的isa指向于元类
struct objc_class {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
} ;
下面来仔细分析一下每一个的结构体
Class(objc_class)
struct objc_class {
Class _Nullable super_class OBJC2_UNAVAILABLE;
const char * _Nonnull name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE;
struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE;
struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE;
struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE;
} OBJC2_UNAVAILABLE;
objc_class结构体中定义了很多变量
- super_class :指向其父类
- name:类名
- version:类的版本信息
- info:类的详情
- instance_size:类的实例大小
- ivars:指向该类的成员变量列表
- methodLists:指向该类的实例方法列表,它将方法选择器和方法实现地址联系起来
- cache:runtime系统中会把被调用的方法缓存到cache中,下次查找时效率更高
- protocols:指向该类的协议列表
Method(objc_method)
struct objc_method {
SEL _Nonnull method_name OBJC2_UNAVAILABLE;
char * _Nullable method_types OBJC2_UNAVAILABLE;
IMP _Nonnull method_imp OBJC2_UNAVAILABLE;
}OBJC2_UNAVAILABLE;
- method_name:方法名
- method_types:方法类型
- method_imp:方法实现
在这个结构体中,我们已经看到了SEL和IMP,说明SEL和IMP其实都是Method的属性。
Category(objc_category)
struct objc_category {
char * _Nonnull category_name OBJC2_UNAVAILABLE;
char * _Nonnull class_name OBJC2_UNAVAILABLE;
struct objc_method_list * _Nullable instance_methods OBJC2_UNAVAILABLE;
struct objc_method_list * _Nullable class_methods OBJC2_UNAVAILABLE;
struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE;
}OBJC2_UNAVAILABLE;
- category_name:分类的名字
- class_name:类的名字
- instance_methods:category中所有给类添加的实例方法的列表
- class_methods:category中所有给类添加的类方法的列表
- protocols:category实现的所有协议的列表
ivar(objc_ivar)
struct objc_ivar {
char * _Nullable ivar_name OBJC2_UNAVAILABLE;
char * _Nullable ivar_type OBJC2_UNAVAILABLE;
int ivar_offset OBJC2_UNAVAILABLE;
#ifdef __LP64__
int space OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
- ivar_name:成员变量名
- ivar_type:成员变量类型
四、runtime应用
对象关联
分类不能添加属性,但是通过关联对象可以实现为分类添加属性
//关联对象
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
//获取关联的对象
id objc_getAssociatedObject(id object, const void *key)
//移除关联的对象
void objc_removeAssociatedObjects(id object)
- id object:被关联的对象
- const void *key:关联的key,要求唯一
- id value:关联的对象
- objc_AssociationPolicy policy:内存管理的策略
//使用案例
@interface NSObject (Category)
@property(nonatomic, copy) NSString *name;
@end
@implementation NSObject (Category)
- (void)setName:(NSString *)name{
//关联对象
objc_setAssociatedObject(self, @"name", name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)name{
//获取关联的值
return objc_getAssociatedObject(self, @"name");
}
@end
方法交换
runtime系统提供了函数用于动态替换类方法和实例方法
method_exchangeImplementations
交换两个方法的实现
+ (void)load{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Method workMethod = class_getInstanceMethod(self, @selector(work));
Method sleepMethod = class_getInstanceMethod(self, @selector(sleep));
method_exchangeImplementations(workMethod, sleepMethod);
});
}
- (void)work{
NSLog(@"the person is working");
}
- (void)sleep{
NSLog(@"the person is sleeping");
}
动态加载方法
当调用一个未实现的方法,或者说发送未知的消息给接收者时候,消息的接受者会调用resolveInstanceMethod
// 默认方法都有两个隐式参数,
void eat(id self,SEL sel)
{
NSLog(@"the person is eating");
}
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
if (sel == @selector(eat)) {
// 动态添加eat方法
// 第一个参数:给哪个类添加方法
// 第二个参数:添加方法的方法编号
// 第三个参数:添加方法的函数实现(函数地址)
// 第四个参数:函数的类型,(返回值+参数类型) v:void @:对象->self :表示SEL->_cmd
class_addMethod(self, @selector(eat), eat, "v@:");
}
return [super resolveInstanceMethod:sel];
}
自动归档
用runtime提供的函数遍历model的属性,并对属性进行encode和decode操作
- (void)encodeWithCoder:(NSCoder *)aCoder{
unsigned int outCount;
Ivar *ivars = class_copyIvarList([self class], &outCount);
for (int i = 0; i < outCount; i++) {
Ivar ivar = ivars[i];
NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
[aCoder encodeObject:[self valueForKey:key] forKey:key];
}
free(ivars);
}
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder{
if (self = [super init]) {
unsigned int outCount;
Ivar *ivars = class_copyIvarList([self class], &outCount);
for (int i = 0; i < outCount; i++) {
Ivar ivar = ivars[i];
NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
[self setValue:[aDecoder decodeObjectForKey:key] forKey:key];
}
free(ivars);
}
return self;
}
字典转模型
遍历自身对象的所有属性,如果属性在字典中有对应的值,则将其赋值
+ (instancetype)modelWithDict:(NSDictionary *)dict{
id model = [[self alloc] init];
unsigned int count = 0;
Ivar *ivars = class_copyIvarList(self, &count);
for (int i = 0 ; i < count; i++) {
Ivar ivar = ivars[i];
NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)];
//截取掉变量前的_
ivarName = [ivarName substringFromIndex:1];
id value = dict[ivarName];
[model setValue:value forKeyPath:ivarName];
}
free(ivars);
return model;
}
参考:完整项目资料下载
网友评论