iOS原理(六)----runtime

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;
}

如果isanonpointer为1,weakly_referenced,has_assoc,has_cxx_dtorhas_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里面的methodspropertiesprotocols是二维数组,是可读可写的,包含了类的初始内容、分类的内容.

class_ro_t里面的baseMethodListbaseProtocolsivarsbaseProperties是一维数组,是只读的,包含了类的初始内容.

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发消息是安全的.

Snip20181114_1.png
Snip20181114_2.png

2.从reveiverClass的cache中查找方法,如果找到方法,调用方法,结束查找.

Snip20181114_3.png

3.从reveiverClass的class_rw_t中查找方法,找到了方法,就将方法缓存.(如果是已经排序的就二分查找,没有就按顺序查找).

Snip20181114_4.png
Snip20181114_5.png

4.从superClass的cache中查找方法,找到了方法,就将方法缓存.

Snip20181114_6.png

5.从superClass的class_rw_t中查找方法,找到了方法,就将方法缓存.

Snip20181114_7.png

6.重复上面4,5步,知道没有父类为止,否则就进入动态方法解析阶段.

Snip20181114_8.png

动态方法解析

1.是否已经动态解析过,已经动态解析过,就直接消息转发.

Snip20181114_9.png

2.调用+resolveInstanceMethod:或者+resolveClassMethod:方法,来动态解析方法.并标记为动态解析过.重新走消息发送流程.

Snip20181114_10.png
Snip20181114_11.png

消息转发

1.调用forwardingTargetForSelector:方法,返回值不为nil的话,消息发送objc_msgSend(返回值, SEL).

2.调用methodSignatureForSelector:方法,返回值不为你来的话,调用forwardInvocation:方法.

3.调用doesNotRecognizeSelector:方法,抛出异常.

Snip20181114_12.png

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)

你可能感兴趣的:(iOS原理(六)----runtime)