iOS原理(六)----runtime
OC是一门动态性比较强的编程语言,允许很多操作推迟到程序运行时再进行.
OC的动态性就是由Runtime来支撑和实现的,Runtime是一套C语言的API,封装了很多动态性相关的函数.
平时编写的OC代码,底层都是转换成了Runtime API进行调用.
具体应用:
- 利用关联对象(AssociatedObject)给分类添加属性
- 遍历类的所有成员变量(修改textfield的占位文字颜色、字典转模型、自动归档解档)
- 交换方法实现(交换系统的方法)
- 利用消息转发机制解决方法找不到的异常问题
isa
在arm64架构之前,isa就是一个普通的指针,存储着Class、Meta-Class对象的内存地址,
从arm64架构开始,对isa进行了优化,变成了一个共用体(union)结构,还使用位域来存储更多的信息.结构如下:
union isa_t
{
Class cls;
uintptr_t bits;
struct {
// 0,代表普通的指针,存储着Class、Meta-Class对象的内存地址,1,代表优化过,使用位域存储更多的信息.
uintptr_t nonpointer : 1;
// 是否有设置过关联对象,如果没有,释放时会更快
uintptr_t has_assoc : 1;
// 是否有C++的析构函数(.cxx_destruct),如果没有,释放时会更快
uintptr_t has_cxx_dtor : 1;
// 存储着Class、Meta-Class对象的内存地址信息
uintptr_t shiftcls : 33;
// 用于在调试时分辨对象是否未完成初始化
uintptr_t magic : 6;
// 是否有被弱引用指向过,如果没有,释放时会更快
uintptr_t weakly_referenced : 1;
// 对象是否正在释放
uintptr_t deallocating : 1;
// 引用计数器是否过大无法存储在isa中,如果为1,那么引用计数会存储在一个叫SideTable的类的属性中
uintptr_t has_sidetable_rc : 1;
// 里面存储的值是引用计数器减1
uintptr_t extra_rc : 19;
};
}
查看源码:
inline void
objc_object::rootDealloc()
{
if (isTaggedPointer()) return; // fixme necessary?
if (fastpath(isa.nonpointer &&
!isa.weakly_referenced &&
!isa.has_assoc &&
!isa.has_cxx_dtor &&
!isa.has_sidetable_rc))
{
assert(!sidetable_present());
free(this);
}
else {
object_dispose((id)this);
}
}
id
object_dispose(id obj)
{
if (!obj) return nil;
objc_destructInstance(obj);
free(obj);
return nil;
}
void *objc_destructInstance(id obj)
{
if (obj) {
// Read all of the flags at once for performance.
bool cxx = obj->hasCxxDtor();
bool assoc = obj->hasAssociatedObjects();
// This order is important.
if (cxx) object_cxxDestruct(obj);
if (assoc) _object_remove_assocations(obj);
obj->clearDeallocating();
}
return obj;
}
如果isa
中nonpointer
为1,weakly_referenced
,has_assoc
,has_cxx_dtor
和has_sidetable_rc
时会直接释放对象,否则会调用c++析构函数object_cxxDestruct
和移除关联对象函数_object_remove_assocations
.
Class的结构
struct objc_class : objc_object {
Class isa;
Class superclass;
// 方法缓存
cache_t cache;
// 具体信息,& FAST_DATA_MASK 得到 class_rw_t
class_data_bits_t bits;
}
struct class_rw_t {
uint32_t flags;
uint32_t version;
// 只读列表,存储类的原始列表
const class_ro_t *ro;
// 方法列表
method_array_t methods;
// 属性列表
property_array_t properties;
// 协议列表
protocol_array_t protocols;
Class firstSubclass;
Class nextSiblingClass;
char *demangledName;
}
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
// 实例对象占用内存大小
uint32_t instanceSize;
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;
}
其中class_rw_t
里面的methods
、properties
、protocols
是二维数组,是可读可写的,包含了类的初始内容、分类的内容.
class_ro_t
里面的baseMethodList
、baseProtocols
、ivars
、baseProperties
是一维数组,是只读的,包含了类的初始内容.
method_t
struct method_t {
// 函数名
SEL name;
// 对返回值和参数的编码
const char *types;
// 指向函数的指针
IMP imp;
}
其中types
是对返回值参数的编码,比如"v16@0:8".
imp为指向函数的指针typedef id _Nullable (*IMP)(id _Nonnull, SEL _Nonnull, ...)
,SEL
为选择器.
cache_t
struct cache_t {
// hashtable
struct bucket_t *_buckets;
// hashtable长度 - 1
mask_t _mask;
// 已缓存的方法数量
mask_t _occupied;
}
struct bucket_t {
// 为_mask & 方法SEl作为key
cache_key_t _key;
// 函数的内存地址
IMP _imp;
}
Class内部结构中有个方法缓存(cache_t),用散列表(哈希表)来缓存曾经调用过的方法,可以提高方法的查找速度.当一次使用缓存时,分配大小为4的缓存,当缓存使用超过4分之3时,扩容为原来的2倍.用_mask & 方法SEl作为key,用过计算的key,发现已经缓存存在时,就将前面的值-1,继续看是否存在,如果不存在就设置缓存.
objc_msgSend
分为消息发送
,动态方法解析
和消息转发
三大阶段.
消息发送
1.判断recivever是否为nil,否则就直接退出,所以说给nil发消息是安全的.
2.从reveiverClass的cache中查找方法,如果找到方法,调用方法,结束查找.
3.从reveiverClass的class_rw_t中查找方法,找到了方法,就将方法缓存.(如果是已经排序的就二分查找,没有就按顺序查找).
4.从superClass的cache中查找方法,找到了方法,就将方法缓存.
5.从superClass的class_rw_t中查找方法,找到了方法,就将方法缓存.
6.重复上面4,5步,知道没有父类为止,否则就进入动态方法解析阶段.
动态方法解析
1.是否已经动态解析过,已经动态解析过,就直接消息转发.
2.调用+resolveInstanceMethod:或者+resolveClassMethod:方法,来动态解析方法.并标记为动态解析过.重新走消息发送流程.
消息转发
1.调用forwardingTargetForSelector:方法,返回值不为nil的话,消息发送objc_msgSend(返回值, SEL).
2.调用methodSignatureForSelector:方法,返回值不为你来的话,调用forwardInvocation:方法.
3.调用doesNotRecognizeSelector:方法,抛出异常.
super
super调用,底层会转换为objc_msgSendSuper2函数的调用,接收2个参数: struct objc_super2和SEL.
struct objc_super2 {
id receiver;
Class current_class;
};
super发送消息,是receiver从current_class的父类开始查找方法.
Runtime API
1.类
动态创建一个类(参数:父类,类名,额外的内存空间):Class objc_allocateClassPair(Class superclass, const char *name, size_t extraBytes)
注册一个类(要在类注册之前添加成员变量):
void objc_registerClassPair(Class cls)
销毁一个类:void objc_disposeClassPair(Class cls)
获取isa指向的Class:
Class object_getClass(id obj)
设置isa指向的Class:
Class object_setClass(id obj, Class cls)
判断一个OC对象是否为Class:
BOOL object_isClass(id obj)
判断一个Class是否为元类:
BOOL class_isMetaClass(Class cls)
获取父类:
Class class_getSuperclass(Class cls)
2 成员变量
获取一个实例变量信息:
Ivar class_getInstanceVariable(Class cls, const char *name)
拷贝实例变量列表(最后需要调用free释放):
Ivar *class_copyIvarList(Class cls, unsigned int *outCount)
设置和获取成员变量的值:
void object_setIvar(id obj, Ivar ivar, id value),
id object_getIvar(id obj, Ivar ivar)
动态添加成员变量(已经注册的类是不能动态添加成员变量的):
BOOL class_addIvar(Class cls, const char * name, size_t size, uint8_t alignment, const char * types)
获取成员变量的相关信息:
const char *ivar_getName(Ivar v),
const char *ivar_getTypeEncoding(Ivar v)
3 属性
获取一个属性:
objc_property_t class_getProperty(Class cls, const char *name)
拷贝属性列表(最后需要调用free释放):
objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)
动态添加属性:
BOOL class_addProperty(Class cls, const char *name, const objc_property_attribute_t *attributes,
unsigned int attributeCount)
动态替换属性:
void class_replaceProperty(Class cls, const char *name, const objc_property_attribute_t *attributes,
unsigned int attributeCount)
获取属性的一些信息:
const char *property_getName(objc_property_t property),
const char *property_getAttributes(objc_property_t property)
4 方法
获得一个实例方法、类方法:
Method class_getInstanceMethod(Class cls, SEL name),
Method class_getClassMethod(Class cls, SEL name)
方法实现相关操作:
IMP class_getMethodImplementation(Class cls, SEL name) ,
IMP method_setImplementation(Method m, IMP imp),
void method_exchangeImplementations(Method m1, Method m2)
拷贝方法列表(最后需要调用free释放):
Method *class_copyMethodList(Class cls, unsigned int *outCount)
动态添加方法:
BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)
动态替换方法:
IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types)
获取方法的相关信息(带有copy的需要调用free去释放):
SEL method_getName(Method m),
IMP method_getImplementation(Method m),
const char *method_getTypeEncoding(Method m),
unsigned int method_getNumberOfArguments(Method m),
char *method_copyReturnType(Method m),
char *method_copyArgumentType(Method m, unsigned int index)
选择器相关:
const char *sel_getName(SEL sel)
SEL sel_registerName(const char *str)
用block作为方法实现:
IMP imp_implementationWithBlock(id block),
id imp_getBlock(IMP anImp),
BOOL imp_removeBlock(IMP anImp)