iOS runtime 理解
源码基于objc4-750
runtime
是什么?
iOS对象在运行时动态创建对象和类、实现消息传递和消息转发的机制.objc_msgSend(objc,SEL);
runtime
原理?
了解runtime
原理前需要了解iOS
一个Class
结构。
Class
结构
NSObject.h
的定义可以知道NSObject
类对象是一个Class isa
。是一个objc_class
结构体。
@interface NSObject <NSObject> {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-interface-ivars"
Class isa OBJC_ISA_AVAILABILITY;
#pragma clang diagnostic pop
}
在objc-runtime-new.h
中Class
的定义是一个objc_class
结构体的指针。objc_class
是继承自objc_object
的结构体(Objective-C 2.0
).
id
是objc_object
结构体的指针。
typedef struct objc_class *Class;
typedef struct objc_object *id;
objc_class
结构体在objc-runtime-new.h
声明:
struct objc_class : objc_object {
// Class ISA;
Class superclass; // 类对象的父类
cache_t cache; // formerly cache pointer and vtable
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
class_rw_t *data() {
return bits.data();
}
... // 省略了其他代码。
// NOT identical to this->ISA when this is a metaclass
Class getMeta() { // 元类
if (isMetaClass()) return (Class)this;
else return this->ISA();
}
bool isRootClass() {
return superclass == nil;
}
bool isRootMetaclass() {
return ISA() == (Class)this;
}
};
cache
是一个cache_t
的结构体。结构体包含函数的IMP
等一些信息,优化方法调用。
struct cache_t {
struct bucket_t *_buckets;
mask_t _mask;
mask_t _occupied;
...
};
struct bucket_t {
private:
// IMP-first is better for arm64e ptrauth and no worse for arm64.
// SEL-first is better for armv7* and i386 and x86_64.
#if __arm64__
MethodCacheIMP _imp;
cache_key_t _key;
#else
cache_key_t _key;
MethodCacheIMP _imp;
#endif
public:
inline cache_key_t key() const { return _key; }
inline IMP imp() const { return (IMP)_imp; }
inline void setKey(cache_key_t newKey) { _key = newKey; }
inline void setImp(IMP newImp) { _imp = newImp; }
void set(cache_key_t newKey, IMP newImp);
};
class_data_bits_t
通过bits & FAST_DATA_MASK
用于class_rw_t* data
的运算。
class_rw_t* data() {
return (class_rw_t *)(bits & FAST_DATA_MASK);
}
data
是class_rw_t
的结构体。
class_rw_t
结构体包含class_ro_t
结构体的指针(一维数组,只读)。包含类的初始内容。方法列表method_array_t
、属性列表property_array_t
、协议protocol_array_t
等一些属性(method_array_t
是一个二维数组)。
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize;
#ifdef __LP64__
uint32_t reserved;
#endif
const uint8_t * ivarLayout;
const char * name; // 类名
method_list_t * baseMethodList; //方法列表
protocol_list_t * baseProtocols; //代理协议列表
const ivar_list_t * ivars; // 属性列表
const uint8_t * weakIvarLayout;
property_list_t *baseProperties;
method_list_t *baseMethods() const {
return baseMethodList;
}
};
struct class_rw_t {
// Be warned that Symbolication knows the layout of this structure.
uint32_t flags;
uint32_t version;
const class_ro_t *ro;
method_array_t methods; // 方法列表
property_array_t properties; // 属性列表。属性的getter、setter方法在methods中
protocol_array_t protocols; // 协议列表
Class firstSubclass;
Class nextSiblingClass;
char *demangledName;
};
objc_object
在objc_private.h
中的声明:
struct objc_object {
private:
isa_t isa;
public:
// ISA() assumes this is NOT a tagged pointer object
Class ISA();
// getIsa() allows this to be a tagged pointer object
Class getIsa();
...
}
runtime.jpg
结合图片和代码分析可知:
objc_object
有一个isa
指针,objc_class
继承自objc_object
也拥有一个isa
指针。类对象的isa
指针指向为元类metaclass
.每个类对象有个superclass
和isa
指针。而实例对象是一个通过[[obj alloc] init]或者[obj new]出来的对象。
实例对象(instance
)的指针指向类对象(class
)。类对象的指针指向根元类(metacalss
)。
根类对象rootclass
的superclass
是nil
.根类对象的指针指向根元类root metaclass
.而根元类的指针指向自己,并且根元类的superclass
为根类对象。
Method
Method
是一个method_t
结构体指针。
struct method_t {
SEL name; //函数名
const char *types; // 函数参数类型
MethodListIMP imp; // 指向函数实现的指针
struct SortBySELAddress :
public std::binary_function<const method_t&,
const method_t&, bool>
{
bool operator() (const method_t& lhs,
const method_t& rhs)
{ return lhs.name < rhs.name; }
};
};
SEL
是一个objc_selector
结构体的指针。保存了函数名的C
字符串。所以在函数声明和实现时,方法名相同和参数列表一样,参数类型不一样的情况下会报错。
/// An opaque type that represents a method selector.
typedef struct objc_selector *SEL;
IMP
是一个函数指针。IMP
函数指针指向了方法实现的首地址。当OC
发起消息后,最终执行代码是由IMP
决定。利用这个特性,当需要重复多次调用函数时,可以绕开消息绑定,直接用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
Method
建立了SEL
和IMP
的关联,当一个对象发送消息后会通过SEL找到对应的IMP执行。
runtime
消息传递
objc_msgSend
是用汇编实现。[obj send]
在编译时会转换成objc_msgSend(obj, send)
函数。
runtime
执行流程:
1.通过obj
的isa
指针找到Class
,如果是类方法则会在metaClass
中查找。
2.在class
的缓存列表中查找是否有对应的方法。没有从methodList
中查找。如果methodList
找不到会沿着继承链一直往根部找,如果找到执行并添加到缓存中。
3.如果找不到会触发消息转发。
消息转发
当runtime
执行流程到第3步时,触发消息转发。消息转发流程如下:
1.程序会调用动态解析函数resolveClassMethod:
或resolveInstanceMethod:
(类方法和实例方法)。在当前函数中可以添加一个对应的函数实现。该函数返回已BOOL
是否可以处理对应的函数,如果return NO;
则执行第2步。
2.调用forwardingTargetForSelector:
备用接收者函数。这个函数可以指定其他接收者。该函数返回一个id类型的参数,如果返回nil则执行第3步。
3.执行methodSignatureForSelector:
函数。该函数需要返回一个方法签名。如果返回nil
,程序异常。如果返回对应的方法签名,则调用forwardInvocation:
函数响应。可以在该函数中指定函数响应的对象。
runtime
应用场景?
1.category
属性关联(通过hash表)。
- (void)setRh_isHidden:(BOOL)rh_isHidden {
objc_setAssociatedObject(self, rh_isShowKey, @(rh_isHidden), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (BOOL)rh_isHidden {
NSNumber *num = objc_getAssociatedObject(self, rh_isShowKey);
return [num boolValue];
}
2.方法交换和添加。
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
SEL systemViewWill = @selector(viewWillAppear:);
SEL swizzViewWill = @selector(rh_viewWillAppear:);
Method systemMethod = class_getInstanceMethod([self class], systemViewWill);
Method swizzMethod = class_getInstanceMethod([self class], swizzViewWill);
BOOL add = class_addMethod(self, systemViewWill, method_getImplementation(swizzMethod), method_getTypeEncoding(swizzMethod));
if (add) {
class_replaceMethod(self, swizzViewWill, method_getImplementation(swizzMethod), method_getTypeEncoding(swizzMethod));
}else {
method_exchangeImplementations(systemMethod, swizzMethod);
}
});
}
- (void)rh_viewWillAppear:(BOOL)animated {
[self rh_viewWillAppear:animated];
}
3.消息转发。
- (id)forwardingTargetForSelector:(SEL)selector {
return _target;
}
- (void)forwardInvocation:(NSInvocation *)invocation {
void *null = NULL;
[invocation setReturnValue:&null];
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
return [NSObject instanceMethodSignatureForSelector:@selector(init)];
}
4.模型和字典转换。
获取到类的属性列表。进行转换
5.KVO。
运行时创建一个监听类的子类。重写监听类的setter方法在setter方法调用willChangeValueForKey:
和didChangeValueForKey:
方法通知监听类。