lovebet体育官网有关Runtime你询问小?RunTime

目录

  • 简介
  • Runtime中之局部数据结构
  • 消息转发
  • 干对象的兑现原理

runtime
Objective-C是因 C 的,它为 C
添加了面向对象的性状。它以多静态语言在编译和链接时期做的事放到了
runtime 运行时来处理

简介

因为Objc是同等宗动态语言,所以其连接惦记方把一些说了算工作打编译连接推迟到运行时。也就是说只有编译器是不够的,还待一个运作时系统
(runtime system) 来施行编译后底代码。这即是 Objective-C Runtime
系统设有的义,它是全 Objc 运行框架的同样片基石。

Runtime其实有点儿只版: “modern” 和 “legacy”。我们现用的 Objective-C
2.0 采用的是本 (Modern) 版的 Runtime 系统,只能运行于 iOS 和 macOS
10.5 之后的 64 位程序中。而 maxOS 较老的32个程序按照使用 Objective-C 1
中之(早期)Legacy 版本的 Runtime
系统。这简单个版本最深的别在于当你重新改一个好像的实例变量的布局时,在前期版本中您用更编译它的子类,而现版虽非需。

在OC中调用一个函数,其实就是通往一个对象(类为是一个目标)发送一长长的信息,比如:
[receiver message]
会面受编译器转化为
objc_msgSend(receiver, selector)
了解Runtime其实就是了解OC动态特性的脚实现,这对咱们知晓OC这门语言非常有必要。

下,你可以下载runtime的源码接下来来与自身同追

对于 C 语言,函数的调用在编译的时光会控制调用哪个函数。

同样、Runtime中的有的数据结构

率先,在runtime源码的objc-private.h文件被我们得以视目标同接近都是一个结构体:

对象及类似的定义

点入我们一个一个翻看

靶的结构体

好看来,对象要就是是一个暗含isa变量的结构体,这个变量主要就是是包含一个指向Class的指针。

isa_t

复来探望Class结构体的实际定义。
以objc-runtime-old.h中,它要涵盖这样有些数据结构:

struct objc_class : objc_object {
    //继承自objc_object的isa指针
    Class superclass;                       //指向父类的指针
    const char *name;                       //类名
    uint32_t version;                       //类的版本信息
    uint32_t info;                          //类信息,提供一些运行期使用的一些标示位
    uint32_t instance_size;                 //类的实例变量的大小
    struct old_ivar_list *ivars;            //类的成员变量链表
    struct old_method_list **methodLists;   //方法定义的链表
    Cache cache;                            //方法缓存(用于消息发送时的查找)
    struct old_protocol_list *protocols;    //协议链表
}

可以看出,objc_class是持续自objc_object的,所以别忘了,objc_class也起一个isa指针。为什么类为闹isa指针呢?我眼前的稿子都提到了,在开创类的时,Runtime其实创建了元类(Meta
Class),,所以类似对象的所属种类就是元类,具体信息可参照这首文章。关于类的信都在是数据结构中,操作类其实就算是操作是结构体。
唯独这是事先的runtime实现,现行版的Runtime源码在objc-runtime-new.h中:

兹版的Class结构体

OC的函数调用叫做消息发送,是动态调用的。在编译的时并无可知控制委调用哪个函数,只有当真的运行的时段才见面根据函数的名号找到相应之函数实现来调用。

cacge_t

cacge_t

cache_t,顾名思义,其实就是缓存,对许为老版本的cache。
_buckets 存储IMP_mask_occupied 对应 vtable

bucket_t

bucket_t

bucket_t 中便是储存了指针与 IMP
的键值对,以当章程寻找的下能对缓存过之方式进行快速响应。

class_data_bits_t

objc_class中极其当的就是是bits,对类似的操作几乎就是绕其进行

struct class_data_bits_t {
    // Values are the FAST_ flags above.
    uintptr_t bits;
    class_rw_t* data() {
        return (class_rw_t *)(bits & FAST_DATA_MASK);
    }
}

做前面objc_classdata方,就是直接以 class_data_bits_t
data 方法返回,返回的是
class_rw_t类型,而之价是bits与FAST_DATA_MASK按位与博的结果。bits在内存中每个位之含义如下:

32位:

32位

64个兼容版:

64员兼容版

64位不兼容版:

64号非兼容版

个中64各项非兼容版每个宏对应如下:

// class is a Swift class
#define FAST_IS_SWIFT           (1UL<<0)
// class's instances requires raw isa
#define FAST_REQUIRES_RAW_ISA   (1UL<<1)
// class or superclass has .cxx_destruct implementation
//   This bit is aligned with isa_t->hasCxxDtor to save an instruction.
#define FAST_HAS_CXX_DTOR       (1UL<<2)
// data pointer
#define FAST_DATA_MASK          0x00007ffffffffff8UL
// class or superclass has .cxx_construct implementation
#define FAST_HAS_CXX_CTOR       (1UL<<47)
// class or superclass has default alloc/allocWithZone: implementation
// Note this is is stored in the metaclass.
#define FAST_HAS_DEFAULT_AWZ    (1UL<<48)
// class or superclass has default retain/release/autorelease/retainCount/
//   _tryRetain/_isDeallocating/retainWeakReference/allowsWeakReference
#define FAST_HAS_DEFAULT_RR     (1UL<<49)
// summary bit for fast alloc path: !hasCxxCtor and 
//   !instancesRequireRawIsa and instanceSize fits into shiftedSize
#define FAST_ALLOC              (1UL<<50)
// instance size in units of 16 bytes
//   or 0 if the instance size is too big in this field
//   This field must be LAST
#define FAST_SHIFTED_SIZE_SHIFT 51

得视,这其中除了FAST_DATA_MASK
是一样段落控件存储数据之外,其它都是故1bit来囤bool值保存信息class_data_bits_t提供了三个方式用于各类操作:getBit,setBitsclearBits,对诺交每个bool值的掩码都发函数封装,如:

    bool hasDefaultRR() {
        return getBit(FAST_HAS_DEFAULT_RR);
    }
    void setHasDefaultRR() {
        setBits(FAST_HAS_DEFAULT_RR);
    }
    void setHasCustomRR() {
        clearBits(FAST_HAS_DEFAULT_RR);
    }

切切实实的您得看源码,我就是非详细贴出来了。

前面我们说了,这个data回去的凡bitsFAST_DATA_MASK各以及博的价值,而这里FAST_DATA_MASK实在就是囤了依赖为class_rw_t的指针。

class_rw_t

class_rw_t

新一看,这个近乎是存储类的不二法门、属性、协议等的,但是我们看来,这里还有一个class_ro_t,再累看

class_ro_t

class_ro_t

梳理一下,尽结构是这么的objc_class包含了class_data_bits_tclass_data_bits_t存储了class_rw_t的指针,而class_rw_t结构体又包含class_ro_t指针。lass_ro_t中的method_list_t,
Ivar_list_t,property_list_t
结构体都继承自entsize_list_tt<Element, List, FlagMask>。结构为xxx_list_t的列表元素结构也xxx_t,命名很整齐。protocol_list_t
与前方三独不等,它存储的是protocol_t *指南针列表,实现比较简单。

entsize_list_tt实现了 non-fragile特性的数组结构。假如苹果在新本子的
SDK 中往
NSObject类增加了有情,NSObject的占据的内存区域会扩大,开发者以前编译出底二进制中之子类就见面暨新的
NSObject
内享有重叠部分。于是以编译期会为instanceStartinstanceSize赋值,确定好编译时每个接近的所占用内存区域开始偏移量和尺寸,这样只是待以子类与基类的及时简单个变量作比即可知道子类是否跟基类有层,如果发,也只是了解子类需要走多少偏移量。

class_ro_t->flags尽管存储了无数以编译时期就规定的切近的音信,也是 ABI
的平等组成部分。

总结:
class_rw_t提供了运转时对类拓展之能力,而class_ro_t存储的大多是近似在编译时就是曾经确定的音讯。二者都怀着来接近的办法、属性(成员变量)、协议相当于消息,不过存储它们的列表实现方式各异。

class_rw_t遭动用的 method_array_t, property_array_t,
protocol_array_t犹连续自list_array_tt<Element, List>,
它可不断扩展,因为她可以储存 list 指针,内容有三种:

  • 一个 entsize_list_tt 指针
  • entsize_list_tt 指针数组

class_rw_t的内容是好于运行时叫动态修改的,可以说运行时对类的展开大都是储存在此处的。

class_rw_t->flags 存储的价值并无是编辑器设置的,其中有些值可能将来会面作
ABI 的等同有些。

demangledName
是电脑语言用于解决实体名称唯一性的一模一样种办法,做法是奔名称被补充加有类型信息,用于从编译器中往链接器传递更多语义信息。

于 NSObject 的初始化了解
isa
Objective-C 对象还是 C 语言结构体,所有的目标还蕴涵一个品类为 isa
的指针
当 ObjC
为呢一个靶分配内存,初始化实例变量后,在这些目标的实例变量的结构体中之第一独就是是
isa。

Category

Category

category_t
存储了种类中得以开展之实例方法、类措施、协议、实例属性与类属性。类特性是
Objective-C 2016 年激增的特性,沾 Swift 的就。所以
category_t受微微成员变量是为配合 Swift 的性状,Objective-C
暂没有提供接口,仅开了底数据结构及的相当。

还有众多数据结构,我就不一一贴出来了,源码中还是可以一直翻的。

在class_data_bits_t 结构体中,只含有一个 64 位的 bits
用于存储和类似有关的消息:
class_data_bits_t bits;
将 bits 与 FAST_DATA_MASK 进行各运算,只取其中的 [3, 47] 位转换成为
class_rw_t * 返回。
ObjC 类中之习性、方法还有按的商等信息都保存于 class_rw_t 中:

其次、消息转发

当一个对象会接一个信时,就会倒正常的方法调用流程。但假如一个目标无法吸收指定消息不时,就会启动所谓”消息转发(message
forwarding)
“机制,通过就同一体制,我们得以告知对象如何处理未知的信。默认情况下,对象吸收及未知之音信,会造成程序崩溃。

信息转发一共来三步:

  1. 动态方法分析
  2. 备用接收者
  3. 完转发
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;
};

动态方法分析

靶在收取及未知之信息时,首先会调用所属类的类方法+resolveInstanceMethod:(实例方法)或者+resolveClassMethod:(类方式)。在这方法吃,我们有机遇吧该未知消息新增一个”处理措施””。不过以该方式的前提是咱已实现了该”处理办法”,只需要在运转时经过class_addMethod函数动态增长到类似里就是可以了。

void functionForMethod1(id self, SEL _cmd) {
    NSLog(@"%@, %p", self, _cmd);
}

+ (BOOL)resolveInstanceMethod:(SEL)sel{
    NSString *selectorString = NSStringFromSelector(sel);
    if ([selectorString isEqualToString:@"method1"]) {
        class_addMethod(self.class, @selector(method1), (IMP)functionForMethod1, "@:");
    }
    return [super resolveInstanceMethod:sel];
}

里饱含一个针对性常量的指针
ro,其中存储了当下相仿以编译期便既规定的特性、方法及随的磋商。

备用接受者

使以达成等同步无法处理消息,则Runtime会继续调整以下方式:

- (id)forwardingTargetForSelector:(SEL)aSelector

如果一个靶实现了之法,并返回一个非nil且非self的结果,则是目标会当信息的新接收者,且消息会受分发到是目标。如果我们没有点名相应的靶子来处理aSelector,则应当调用父类的贯彻来回到结果。

如:

- (id)forwardingTargetForSelector:(SEL)aSelector{
    return [Test2 new];
}

这发送的消息就见面付给Test2的一个对象

注意:如果想为换类方法的接受者,需要覆写
+ (id)forwardingTargetForSelector:(SEL)aSelector方法,并回回类对象:

此地编译器没有代码补全提醒,且若以文档中凡寻找不交者法的,但是透过试验确实是来夫措施的,且会顺风转发类方法。

在编译期间好像的构造被之 class_data_bits_t *data 指向的凡一个
class_ro_t * 指针:
然后在加载 ObjC 运行时之时光调用 realizeClass 方法:

总体信息转发

设若当达成同步还未可知处理未知消息,则唯一能够开的就算是启用完整的消息转发机制了。此时会调用以下办法:

- (void)forwardInvocation:(NSInvocation *)anInvocation

此处要小心的是参数anInvocation凡是由哪的来的吧?其实际forwardInvocation:信息发送前,Runtime系统会向目标发送methodSignatureForSelector:消息,并得到到回的计签名用于转移NSInvocation靶。所以我们当重新写forwardInvocation:的以也使还写methodSignatureForSelector:方法,否则会弃大。

立马同一步转发以及第二步转发的基本点区别就是是,它好指定多只目标开展转发,且这些目标还亟需贯彻相应的计,否则还是会丢掉来老。如:

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
    NSString *sel = NSStringFromSelector(aSelector);
    if ([sel isEqualToString:@"add"]) {
        return [NSMethodSignature signatureWithObjCTypes:"v:@"];
    }
    return [super methodSignatureForSelector:aSelector];
}

- (void)forwardInvocation:(NSInvocation *)anInvocation{
    SEL sel = anInvocation.selector;
    if ([NSStringFromSelector(sel) isEqualToString:@"add"]) {
        [anInvocation invokeWithTarget:[Test2 new]];
        [anInvocation invokeWithTarget:[Test3 new]];
    }
}

这Test2和Test3点儿独八九不离十的靶子都见面转接这长长的消息。

  1. 从 class_data_bits_t 调用 data 方法,将结果由 class_rw_t
    强制转换为 class_ro_t 指针
  2. 初始化一个 class_rw_t 结构体
  3. 装结构体 ro 的价和 flag
  4. 终极设置科学的 data。

老三、关联对象的落实原理(Associated Objects)

此间我就算非介绍涉对象的用了,网上有关博客有诸多,这里我们介绍涉对象是使将一个目标关系起来的。
咱俩一直看关系对象相关的老三单道吧:

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

objc_setAssociatedObject

咱们一直看objc-runtime.mm中之源码

void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy) {
    _object_set_associative_reference(object, (void *)key, value, policy);
}

void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {
    // retain the new value (if any) outside the lock.
    ObjcAssociation old_association(0, nil);
    id new_value = value ? acquireValue(value, policy) : nil;
    {
        AssociationsManager manager;
        AssociationsHashMap &associations(manager.associations());
        disguised_ptr_t disguised_object = DISGUISE(object);
             ...
    }
    if (old_association.hasValue()) ReleaseValue()(old_association);
}

这边我概括了差不多底实现代码,我们着重看她的贯彻原理就是好。

要注意这里的几乎单类和数据结构:

  • AssociationsManager
  • AssociationsHashMap
  • ObjcAssociationMap
  • ObjcAssociation

AssociationsManager 在源代码中的定义是如此的:

spinlock_t AssociationsManagerLock;

class AssociationsManager {
    // associative references: object pointer -> PtrPtrHashMap.
    static AssociationsHashMap *_map;
public:
    AssociationsManager()   { AssociationsManagerLock.lock(); }
    ~AssociationsManager()  { AssociationsManagerLock.unlock(); }

    AssociationsHashMap &associations() {
        if (_map == NULL)
            _map = new AssociationsHashMap();
        return *_map;
    }
};

它们保护了spinlock_t
AssociationsHashMap的单例,初始化它的时段会调用 lock.lock()
方法,在析构时见面调用lock.unlock(),而 associations
方法用于博一个大局的 AssociationsHashMap 单例。

也就是说 AssociationsManager
通过具有一个自旋锁
spinlock_t 保证对 AssociationsHashMap
的操作是线程安全的,即老是只见面来一个线程对 AssociationsHashMap
进行操作

const class_ro_t *ro = (const class_ro_t *)cls->data();
class_rw_t *rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1);
rw->ro = ro;
rw->flags = RW_REALIZED|RW_REALIZING;
cls->setData(rw);

怎么存储ObjcAssociation

AssociationsHashMap

class AssociationsHashMap : public unordered_map<disguised_ptr_t, ObjectAssociationMap *, DisguisedPointerHash, DisguisedPointerEqual, AssociationsHashMapAllocator> {
    public:
        void *operator new(size_t n) { return ::malloc(n); }
        void operator delete(void *ptr) { ::free(ptr); }
    };

AssociationsHashMap用来保存于目标的 disguised_ptr_t
ObjectAssociationMap 的映射。

ObjectAssociationMap

class ObjectAssociationMap : public std::map<void *, ObjcAssociation, ObjectPointerLess, ObjectAssociationMapAllocator> {
    public:
        void *operator new(size_t n) { return ::malloc(n); }
        void operator delete(void *ptr) { ::free(ptr); }
    };

ObjectAssociationMap则保留了打key 到干对象ObjcAssociation
的投射,这个数据结构保存了目前目标对应的装有涉嫌对象

ObjcAssociation

class ObjcAssociation {
        uintptr_t _policy;
        id _value;
    public:
        ObjcAssociation(uintptr_t policy, id value) : _policy(policy), _value(value) {}
        ObjcAssociation() : _policy(0), _value(nil) {}

        uintptr_t policy() const { return _policy; }
        id value() const { return _value; }

        bool hasValue() { return _value != nil; }
    };

ObjcAssociation 包含了 policy以及 value

推一个大概的例证:

        NSObject *obj = [NSObject new];
        objc_setAssociatedObject(obj, @selector(hello), @"Hello", OBJC_ASSOCIATION_RETAIN_NONATOMIC);

此地的涉对象 ObjcAssociation(OBJC_ASSOCIATION_RETAIN_NONATOMIC,
@”Hello”) 在内存中是这般存储的:

associateobjcect

哼了,我们回来对 objc_setAssociatedObject艺术的辨析

void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {
    // retain the new value (if any) outside the lock.
    ObjcAssociation old_association(0, nil);
    id new_value = value ? acquireValue(value, policy) : nil;
    {
        AssociationsManager manager;
        AssociationsHashMap &associations(manager.associations());
        disguised_ptr_t disguised_object = DISGUISE(object);
        if (new_value) {
            // break any existing association.
            AssociationsHashMap::iterator i = associations.find(disguised_object);
            if (i != associations.end()) {
                // secondary table exists
                ObjectAssociationMap *refs = i->second;
                ObjectAssociationMap::iterator j = refs->find(key);
                if (j != refs->end()) {
                    old_association = j->second;
                    j->second = ObjcAssociation(policy, new_value);
                } else {
                    (*refs)[key] = ObjcAssociation(policy, new_value);
                }
            } else {
                // create the new association (first time).
                ObjectAssociationMap *refs = new ObjectAssociationMap;
                associations[disguised_object] = refs;
                (*refs)[key] = ObjcAssociation(policy, new_value);
                object->setHasAssociatedObjects();
            }
        } else {
            // setting the association to nil breaks the association.
            AssociationsHashMap::iterator i = associations.find(disguised_object);
            if (i !=  associations.end()) {
                ObjectAssociationMap *refs = i->second;
                ObjectAssociationMap::iterator j = refs->find(key);
                if (j != refs->end()) {
                    old_association = j->second;
                    refs->erase(j);
                }
            }
        }
    }
    // release the old value (outside of the lock).
    if (old_association.hasValue()) ReleaseValue()(old_association);
}
  1. 使用 old_association(0, nil) 创建一个现的 ObjcAssociation
    对象(用于所有原有的涉嫌对象,方便于法调用的终极释放值)

new_value != nil的气象下

  1. 调用 acquireValuenew_value进行retain 或者 copy

static id acquireValue(id value, uintptr_t policy) {
    switch (policy & 0xFF) {
    case OBJC_ASSOCIATION_SETTER_RETAIN:
        return objc_retain(value);
    case OBJC_ASSOCIATION_SETTER_COPY:
        return ((id(*)(id, SEL))objc_msgSend)(value, SEL_copy);
    }
    return value;
}
  1. 初始化一个
    AssociationsManager,并收获唯一的保留关联对象的哈希表AssociationsHashMap

  2. 事先下 DISGUISE(object) 作为 key 寻找对应的 ObjectAssociationMap

  3. 苟无找到,初始化一个 ObjectAssociationMap,再实例化
    ObjcAssociation 对象上加至 Map 中,并调用 setHasAssociatedObjects
    方法(它见面以 isa 结构体中之符号位 has_assoc 标记为
    true),表明当前目标涵盖关联对象

ObjectAssociationMap *refs = new ObjectAssociationMap;
associations[disguised_object] = refs;
(*refs)[key] = ObjcAssociation(policy, new_value);
object->setHasAssociatedObjects();
  1. 如若找到了相应的 ObjectAssociationMap,就要扣 key
    是否在了,由此来支配是翻新原有的关联对象,还是多一个

ObjectAssociationMap *refs = i->second;
ObjectAssociationMap::iterator j = refs->find(key);
if (j != refs->end()) {
    old_association = j->second;
    j->second = ObjcAssociation(policy, new_value);
} else {
    (*refs)[key] = ObjcAssociation(policy, new_value);
}
  1. 最后,如果原本的干对象有值的讲话,会调用 ReleaseValue()
    释放关联对象的价值

new_value == nil的情下,其实就是调用 erase 方法,擦除
ObjectAssociationMap 中 key 对应的节点,删除对承诺key的涉嫌对象。

objc_getAssociatedObject
前面objc_setAssociatedObject已经详尽介绍了,下面这片独道就是特别爱掌握了。

id objc_getAssociatedObject(id object, const void *key) {
    return _object_get_associative_reference(object, (void *)key);
}

id _object_get_associative_reference(id object, void *key) {
    id value = nil;
    uintptr_t policy = OBJC_ASSOCIATION_ASSIGN;
    {
        AssociationsManager manager;
        AssociationsHashMap &associations(manager.associations());
        disguised_ptr_t disguised_object = DISGUISE(object);
        AssociationsHashMap::iterator i = associations.find(disguised_object);
        if (i != associations.end()) {
            ObjectAssociationMap *refs = i->second;
            ObjectAssociationMap::iterator j = refs->find(key);
            if (j != refs->end()) {
                ObjcAssociation &entry = j->second;
                value = entry.value();
                policy = entry.policy();
                if (policy & OBJC_ASSOCIATION_GETTER_RETAIN) {
                    objc_retain(value);
                }
            }
        }
    }
    if (value && (policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE)) {
        objc_autorelease(value);
    }
    return value;
}

它们的逻辑和objc_setAssociatedObject差不多

  1. 获取静态变量AssociationsHashMap

  2. DISGUISE(object)为 key 查找AssociationsHashMap

  3. void *keykey查找ObjcAssociation

  4. 根据 policy调用相应的方法

if (policy & OBJC_ASSOCIATION_GETTER_RETAIN) {
      objc_retain(value);
 }

if (value && (policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE)) {
     objc_autorelease(value);
 }
  1. 返关联对象 ObjcAssociation 的价

objc_removeAssociatedObjects

直接放代码吧

void objc_removeAssociatedObjects(id object) 
{
    if (object && object->hasAssociatedObjects()) {
        _object_remove_assocations(object);
    }
}

void _object_remove_assocations(id object) {
    vector< ObjcAssociation,ObjcAllocator<ObjcAssociation> > elements;
    {
        AssociationsManager manager;
        AssociationsHashMap &associations(manager.associations());
        if (associations.size() == 0) return;
        disguised_ptr_t disguised_object = DISGUISE(object);
        AssociationsHashMap::iterator i = associations.find(disguised_object);
        if (i != associations.end()) {
            // copy all of the associations that need to be removed.
            ObjectAssociationMap *refs = i->second;
            for (ObjectAssociationMap::iterator j = refs->begin(), end = refs->end(); j != end; ++j) {
                elements.push_back(j->second);
            }
            // remove the secondary table.
            delete refs;
            associations.erase(i);
        }
    }
    // the calls to releaseValue() happen outside of the lock.
    for_each(elements.begin(), elements.end(), ReleaseValue());
}

看此间自己想也无啥用说之了,唯一需要注意一点的即是此处当去之前,它加了个判断if (object && object->hasAssociatedObjects())
俺们来瞧这个hasAssociatedObjects

objc_object::hasAssociatedObjects()
{
    if (isTaggedPointer()) return true;
    if (isa.nonpointer) return isa.has_assoc;
    return true;
}

如果是TaggedPointer,则回true,正常对象则是依据isa的记位来判断是否是涉嫌对象。

唯独,在当时段代码运行之后 class_rw_t
中之办法,属性和协和列表均为空。这时要 realizeClass 调用
methodizeClass
方法来拿看似自己实现之章程(包括分类)、属性和准的情商加载到 methods、
properties 和 protocols 列表中。

总结

Runtime是永葆OC的一个分外强大的库,OC的重重特色还是乘于Runtime,所以想只要再次好之控这宗语言,对Runtime的解是必需之。

末,文中有啊错的地方要大家指出,希望以及豪门共同进步。

当实例方法为调用时,它若透过自己装有的 isa 来寻觅对应之类似,然后于此处的
class_data_bits_t 结构体中检索对应措施的实现。同时,每一个
objc_class 也有一个对自己的父类的指针 super_class
用来寻觅继承的法子。

信之调用过程

runtime进行曲,objc_msgSend的前生今生(二)

伪代码

// 首先看一下objc_msgSend的方法实现的伪代码
id objc_msgSend(id self, SEL op, ...) {
   if (!self) return nil;
   // 关键代码(a)
   IMP imp = class_getMethodImplementation(self->isa, SEL op);
   imp(self, op, ...); // 调用这个函数,伪代码...
}
// 查找IMP
IMP class_getMethodImplementation(Class cls, SEL sel) {
    if (!cls || !sel) return nil;
    IMP imp = lookUpImpOrNil(cls, sel);
    if (!imp) {
      ... // 执行动态绑定
    }
    IMP imp = lookUpImpOrNil(cls, sel);
    if (!imp) return _objc_msgForward; // 这个是用于消息转发的
    return imp;
}
// 遍历继承链,查找IMP
IMP lookUpImpOrNil(Class cls, SEL sel) {
    if (!cls->initialize()) {
        _class_initialize(cls);
    }
    Class curClass = cls;
    IMP imp = nil;
    do { // 先查缓存,缓存没有时重建,仍旧没有则向父类查询
        if (!curClass) break;
        if (!curClass->cache) fill_cache(cls, curClass);
        imp = cache_getImp(curClass, sel);
        if (imp) break;
    } while (curClass = curClass->superclass); // 关键代码(b)
    return imp;
}

objc_msgSend为什么采取汇编语言

Objective-C
消息发送和转化机制原理
其实在
objc-msg-x86_64.s
中带有了差不多个版的 objc_msgSend
方法,它们是根据返回值的类型以及调用者的门类分别处理的:

  • objc_msgSendSuper:向父类发消息,返回值类型为 id
  • objc_msgSend_fpret:返回值类型为 floating-point,其中包含
    objc_msgSend_fp2ret 入口处理回来值类型为 long double 的状态
  • objc_msgSend_stret:返回值为结构体
  • objc_msgSendSuper_stret:向父类发消息,返回值类型为结构体

当需要发送信息不时,编译器会转移中间代码,根据气象分别调用 objc_msgSend,
objc_msgSend_stret, objc_msgSendSuper, 或 objc_msgSendSuper_stret
其中某。
立马吗是怎么 objc_msgSend 要用汇编语言而无是 OC、C 或 C++
语言来落实,为单独一个计定义满足不了多种类型返回值,有的艺术返回
id,有的返回
int。考虑到不同类别参数返回值排列组合映射不同措施签名(method
signature)的问题,那 switch 语词得老长了。。。这些由可以总结为
Calling
Convention,也就是说函数调用者跟于调用者必须预约好参数和归值在不同架构处理器上之存取规则,比如参数是因何种顺序存储在栈上,或是存储于怎么寄存器上。除此之外还发出另外原因,比如该只是易参数用汇编处理起来无比利于,因为找到
IMP 地址后参数还在栈上。要是用 C++ 传递可易参数那即便悲剧了,prologue
机制会抓瞎地址(比如 i386 上为存储 ebp 向后运动 4byte),最后还要用
epilogue 打扫战场。而且汇编程序实施效率高,在 Objective-C Runtime
中调用频率比较高之函数好多且为此汇编写的。

仿佛方式的落实又是哪寻找并且调用的啊?

亟待引入元类来确保不管类还是对象都能通过同样的体制查找方法的兑现。
让每一个像样的 isa
指向对应的元类,这样即使高达了而类似方式与实例方法的调用机制同的目的:

  • 实例方法调用时,通过对象的 isa 在类似中获方式的兑现
  • 类似措施调用时,通过类似的 isa 在元类中取得方式的实现

办法决议

于源代码看 ObjC
中信息之出殡
选择子在目前接近和父类中都未曾找到实现,就进了法决议(method
resolve)的历程:

if (resolver  &&  !triedResolver) {
    _class_resolveMethod(cls, sel, inst);
    triedResolver = YES;
    goto retry;
}

立有些代码调用 _class_resolveMethod 来分析并未找到实现之方法。

void _class_resolveMethod(Class cls, SEL sel, id inst)
{
    if (! cls->isMetaClass()) {
        _class_resolveInstanceMethod(cls, sel, inst);
    }
    else {
        _class_resolveClassMethod(cls, sel, inst);
        if (!lookUpImpOrNil(cls, sel, inst,
                            NO/*initialize*/, YES/*cache*/, NO/*resolver*/))
        {
            _class_resolveInstanceMethod(cls, sel, inst);
        }
    }
}

信息转发forwarding

以缓存、当前类、父类以及 resolveInstanceMethod:
都无缓解实现查找的问题常常,Objective-C
还也咱提供了最终一不善翻身的机遇,进行艺术转化:
Objective-C
消息发送和转向机制原理
信转发路径的逻辑,概括如下:
1、先调用 forwardingTargetForSelector 方法取得新的 target 作为 receiver
重新履行 selector,如果回去的情未合法(为 nil 或者跟旧 receiver
一样),那便上第二步。

2、调用 methodSignatureForSelector
获取方式签名后,判断返回类型信息是否正确,再调用 forwardInvocation 执行
NSInvocation 对象,并以结果返回。如果目标没兑现
methodSignatureForSelector 方法,进入第三步。

3、调用 doesNotRecognizeSelector 方法。

lovebet体育官网 1

消息发送和转化路径流程图.png

RunTime应用实例

电动型分析

MJExtension
jsonmodel

最主要代码
NSObject+MJProperty.m

............. 
        /**遍历这个类的父类*/
        [self enumerateClassesWithBlock:^(__unsafe_unretained Class c, BOOL *stop) {
            // 1.获得所有的成员变量
            unsigned int outCount = 0;
            /**
                class_copyIvarList 成员变量,提示有很多第三方框架会使用 Ivar,能够获得更多的信息
                但是:在 swift 中,由于语法结构的变化,使用 Ivar 非常不稳定,经常会崩溃!
                class_copyPropertyList 属性
                class_copyMethodList 方法
                class_copyProtocolList 协议
                */
            objc_property_t *properties = class_copyPropertyList(c, &outCount);

            // 2.遍历每一个成员变量
            for (unsigned int i = 0; i<outCount; i++) {
                MJProperty *property = [MJProperty cachedPropertyWithProperty:properties[i]];
                property.srcClass = c;
                [property setKey:[self propertyKey:property.name] forClass:self];
                [property setObjectClassInArray:[self propertyObjectClassInArray:property.name] forClass:self];
                [cachedProperties addObject:property];
            }

            // 3.释放内存
            free(properties);
        }];
............. 

JSONModel.m

.............        
        unsigned int propertyCount;
        objc_property_t *properties = class_copyPropertyList(class, &propertyCount);

        //loop over the class properties
        for (unsigned int i = 0; i < propertyCount; i++) {

            JSONModelClassProperty* p = [[JSONModelClassProperty alloc] init];

            //get property name
            objc_property_t property = properties[i];
            const char *propertyName = property_getName(property);
            p.name = @(propertyName);

            //JMLog(@"property: %@", p.name);

            //get property attributes
            const char *attrs = property_getAttributes(property);
            NSString* propertyAttributes = @(attrs);
            NSArray* attributeItems = [propertyAttributes componentsSeparatedByString:@","];
.............

道交换

Method
Swizzling

#import <objc/runtime.h>

@implementation UIViewController (Tracking)

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class class = [self class];

        SEL originalSelector = @selector(viewWillAppear:);
        SEL swizzledSelector = @selector(xxx_viewWillAppear:);

        Method originalMethod = class_getInstanceMethod(class, originalSelector);
        Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);

        // When swizzling a class method, use the following:
        // Class class = object_getClass((id)self);
        // ...
        // Method originalMethod = class_getClassMethod(class, originalSelector);
        // Method swizzledMethod = class_getClassMethod(class, swizzledSelector);

        BOOL didAddMethod =
            class_addMethod(class,
                originalSelector,
                method_getImplementation(swizzledMethod),
                method_getTypeEncoding(swizzledMethod));

        if (didAddMethod) {
            class_replaceMethod(class,
                swizzledSelector,
                method_getImplementation(originalMethod),
                method_getTypeEncoding(originalMethod));
        } else {
            method_exchangeImplementations(originalMethod, swizzledMethod);
        }
    });
}

#pragma mark - Method Swizzling

- (void)xxx_viewWillAppear:(BOOL)animated {
    [self xxx_viewWillAppear:animated];
    NSLog(@"viewWillAppear: %@", self);
}

@end

为什么当load方法被补充加
公真正了解 load
方法么?
load 作为 Objective-C
中之一个措施,与其他措施有十分十分之不比。它不过是一个每当整整文件被加载到运行时,在
main 函数调用之前让 ObjC 运行时调用的钩子方法。

+load 方法会在main 函数运行前调用,每个接近、分类的load
方法还见面叫调用。被调用时,所有的 framework
都已加载到了运行时吃(但有些类或还非加载)。在一个好像的 load
方法被调用其他类似的法,如果让调用的类似还免load,并无见面沾被调用的好像的load
方法。

深切了解Objective-C:Category
附加category到接近的做事会晤先于+load方法的施行
+load的实施顺序是先类,后category,而category的+load执行各个是基于编译顺序决定的。

也Category添加属性

深入理解Objective-C:Category
咱理解在category里面凡是无能为力为category添加实例变量的。但是咱多时节需要在category中添加及目标关系的价,这个时节可以求助关联对象来兑现。

MyClass+Category1.h:

#import "MyClass.h"

@interface MyClass (Category1)

@property(nonatomic,copy) NSString *name;

@end

MyClass+Category1.m:

#import "MyClass+Category1.h"
#import <objc/runtime.h>

@implementation MyClass (Category1)

+ (void)load
{
    NSLog(@"%@",@"load in Category1");
}

- (void)setName:(NSString *)name
{
    objc_setAssociatedObject(self,
                             "name",
                             name,
                             OBJC_ASSOCIATION_COPY);
}

- (NSString*)name
{
    NSString *nameObject = objc_getAssociatedObject(self, "name");
    return nameObject;
}

@end

提到对象 AssociatedObject
完全解析
事关对象又是什么样促成以管理的为:

  • 涉及对象实际就是是 ObjcAssociation 对象
  • 波及对象由 AssociationsManager 管理并在 AssociationsHashMap 存储
  • 靶的指针以及该对许 ObjectAssociationMap 以键值对之花样储存于
    AssociationsHashMap 中
  • ObjectAssociationMap 则是用以存储关联对象的数据结构
  • 各个一个目标还出一个标记位 has_assoc 指示对象是不是包含关联对象

采用信息转发实现热修复

JSPatch
实现原理详解
当调用一个 NSObject
对象非有的办法时,并无见面这抛来怪,而是会由此差不多重叠转发,层层调用对象的
-resolveInstanceMethod:, -forwardingTargetForSelector:,
-methodSignatureForSelector:, -forwardInvocation: 等办法,其中最后
-forwardInvocation: 是会来一个 NSInvocation 对象,这个 NSInvocation
对象保存了此措施调用的所有消息,包括 Selector
名,参数和归值类型,最着重之是有所有参数值,可以从这个 NSInvocation
对象里将到调用的具备参数值。我们得想方法吃每个需要吃 JS
替换的点子调用最后还调动到
-forwardInvocation:,就可缓解无法将到参数值的题材了。

引用

Objective-C
消息发送和转化机制原理

从今源代码看 ObjC
中信息之出殡

涉及对象 AssociatedObject
完全解析

深入明Objective-C:Category

runtime

Method
Swizzling

Objective-C的hook方案(一): Method
Swizzling

JSPatch
实现原理详解