至于Runtime你打探多少?

目录

  • 简介
  • Runtime中之一对数据结构
  • 信息转发
  • 涉对象的落实原理

注:这是一样首2014年本身形容的乐评。周杰伦新专辑《哎哟,不错哦》已让2014年底批发。

简介

以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的源码下一场来与自己一同探索

2014
年也接近尾声了,脑子里却一直挂在一个问号:周杰伦的新专辑什么时批发?
这同样好奇心从上张新专辑《十二初发》发行后虽起来发生。

平等、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结构体

这种思想就吓于苹果粉丝刚拿到炙手可热的金色 iPhone 5s,又于怀疑新 iPhone
6 的发布日。可是iPhone 6都已经闹了呀,苹果粉丝非常激动地可望到了 iPhone 6
和 iPhone 6 Plus 两舒缓夷舰机,还亲眼目睹了苹果 CEO 揭开 Apple Watch
的秘密面纱(2015年初上市)。这是千篇一律种植粉丝和偶像中的平衡关系,偶像会被他的粉丝有承诺,例如苹果商店同年时的新产品,(如今方便之
iPhone,iPad,以前站于第一线的 iPod
等)这些承诺若一个伟人的“磁铁”,吸引粉丝。当诺实现的工夫越贴近,关注度犹如定时炸弹一样给引爆。那么问题来了,要是承诺并未兑现吗?这个问题就是得由周杰伦先生对了。

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
是电脑语言用于解决实体名称唯一性的相同种办法,做法是往名称被补充加有类型信息,用于从编译器中为链接器传递更多语义信息。

而,我或者确实不敢张口就喷周杰伦。相比周董迟到的特辑,我立首《十二新发》的乐评本应该早以少数年前就是起了。今天才开始敲键盘真是感觉有点不好意思。我获取标题为《迟却休到底尽迟的乐评》是以,虽然时隔将近两年自己还未曾写就张专辑的听感评价,但周同学(方文山叫法)的新专辑却迟迟也不出现。

Category

Category

category_t
存储了花色吃可拓展之实例方法、类方式、协议、实例属性与类属性。类性质是
Objective-C 2016 年新增的风味,沾 Swift 的特。所以
category_t着多少成员变量是为配合 Swift 的表征,Objective-C
暂没有提供接口,仅开了底数据结构及之匹配。

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

个别年过去了,我对整张专辑的发呢产生一些奇特之转变。不敢说自己的评算得上啊业界规范,但自己不过认定的凡,若是少于年前自己勾勒了《十二新发》测评,观点与今天会见来格外老的进出。

第二、消息转发

当一个靶能够接一个音不时,就见面活动正常的办法调用流程。但如若一个靶无法接指定消息不时,就会启动所谓”信息转发(message
forwarding)
“机制,通过就同建制,我们得告诉对象如何处理未知之音。默认情况下,对象吸收及未知之信息,会导致程序崩溃。

信息转发一共来三步:

  1. 动态方法分析
  2. 备用接收者
  3. 总体转发

第一,不可否认的凡,《十二初发》是一模一样布置当场让周杰伦粉丝万众期待的专辑。在初期放出主打《红尘客栈》和《明明虽》后,歌迷可以说凡是欢腾,激动万分。我还记当时恰放出
30
秒的《红尘客栈》抢先版,歌迷们反映很激烈,有些人说先的周杰伦回来了,有些人说唱腔很过硬,还有大部分人口经就三十秒,浮想有了整张专辑的中纬度。我立刻便是“有些人”中的分子。那句“剑出鞘
恩怨了 谁笑”,让歌迷眉毛稍皱,心跳加速,拳头紧握。

动态方法分析

靶在吸收至未知之音时,首先会见调用所属类的类方法+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];
}

周杰伦《十二新发》专辑封面

备用接受者

设在上同步无法处理消息,则Runtime会继续调整以下方法:

- (id)forwardingTargetForSelector:(SEL)aSelector

假若一个对象实现了这法,并赶回一个非nil且非self的结果,则是目标见面当信息之初接收者,且消息会给分发及是目标。如果我们从没点名相应的目标来拍卖aSelector,则应调用父类的贯彻来回到结果。

如:

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

这会儿殡葬的消息就是见面付给Test2的一个目标

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

此处编译器没有代码补全提醒,且若当文档中凡寻找不交是方法的,但是透过实验确实是起是主意的,且会如愿转发类方法。

红尘客栈 �—— 欠我太久的华民歌

完全信息转发

要是当上平等步还非克处理未知消息,则唯一能做的即使是启用完整的信转发机制了。此时会晤调用以下措施:

- (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点儿独像样的靶子还见面转接这条信息。

千帆竞发,只有钢琴声,这马上叫某些歌迷想到了很久以前的《东风破》。这是周杰伦为数不多的故钢琴旋律开头的中华风歌曲。
隐约的古筝拨弦衬托着周杰伦略有磁性的声息引出正歌部分,鼓点很爱,感觉一切都在为全曲的副歌高潮做铺垫。“剑出鞘
恩怨了 谁笑”正式带副歌,“红尘客栈风似刀 骤雨落
宿命敲”把情绪持续朝着上推动,周杰伦拉长嗓音的唱法在以前也十分少出现,可以听出周杰伦一方面有想念了练习和改良自己之唱腔,另一方面对歌曲的高度投入程度。第四段子的副歌也听得有周杰伦对歌曲唱法的用功的处在。“我说缘分呐”这词用这同种植感叹的音吐生,给丁一致种惊喜,在音频中也休为突然。有更的歌迷会联想到《跨时代》专辑里的《雨下一整晚》,其中“嗨”的一模一样名叹息曾叫整治篇歌唱加分不少。当曲进入到第二段子副歌时,没有惊喜之视听更的提高,更多的凡前段副歌的“复制粘贴”。结尾的钢琴及二胡结合描绘出一番凄惨的究竟,最后一名誉钢琴键落下,为全故事写及一个句号。这篇歌唱完全感到去我梦想的高潮最高点还不同那么一点点,于是,总是一不小心就惊呆地窥见同样一体又放开了了,不知都循环了聊遍,好像从没听见心里想抓到的那一刻。

其三、关联对象的落实原理(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
进行操作

马上是特辑的第二波主打,一首抒情的慢歌。周杰伦的歌名明明就是是独“噱头”,明明虽?明明就是什么?
(明明就算其比温柔)如同红尘客栈,开头为是同等截钢琴旋律。正歌有第一句刚出,我之心地就脆弱了,感觉周杰伦的喉咙钻到了而的胸臆,心痒痒的,暖暖的。这时,杰伦的粉丝将超过出来说道:“这就是独立的周氏情歌。”虽说周式情歌没有一个正经的概念,也未是说周式情歌都是一个特征,但是被听者的发就是是,这篇歌,这样的韵律,配这样的唱腔,只有周杰伦诠释的最好好。“远方传来风笛 我单于一点一滴来您的音”这句衔接正歌与副歌,却成了整理篇歌唱的点睛之笔。粉丝还惊叹:“这是周杰伦熟悉的意味,以前的周杰伦回来了!”

怎样存储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的标志位来判断是否在关联对象。

《明明尽管》与《最丰富的影片》最后之升华极其相似,都是把真音推至巅峰,接着真假音转换。连续升高音“明明即
明明就 明明就”
的做法确实还被人口奇怪(听出一条焦虑与绝望)。如果您试试的言辞,就理解爬三截高音是产生多辛苦了。嗓子累,心又累。“明明即外比暖柔 也许他会让你还多”,直接真假音切换,效果似乎《最丰富的影片》中“再吃我有限分钟
让我拿记忆结成冰 别融化了泪水 妳妆都花了如自我岂记 记得妳叫自己遗忘了吧
记得妳叫自己忘记了吧”。

总结

Runtime是支持OC的一个雅强大的库,OC的无数表征都是借助于Runtime,所以想使再次好之控制这门语言,对Runtime的理解是少不了的。

说到底,文中有什么错误的地方想大家指出,希望与大家共同进步。

任凭了几不折不扣后,脑子里忽然觉得仿佛冒出另外一篇歌唱(这种光景在纵就首歌之前期常常出现),于是拼命想,联想到的是陈奕迅的《淘汰》。我单听在《明明就算》,一边哼着《淘汰》,发现有限首歌唱几乎会到组合。我发生硌激动,偷笑周董有偷工减料“环保”写歌。过了几天,我失去百度“明明便
淘汰”,
发现有些耳尖的歌迷有着跟本身一般的想法,我为尽管放松了同一丁暴,应证了我的判断性。不过,要在此地声明的凡,虽说《明明即》与《淘汰》有几区划相似,但切莫克肯定周杰伦有抄袭行为。毕竟《淘汰》和《明明即》本来都是周杰伦写的。这次的“环保”写歌吗呈现出了周杰伦的音乐才情。相近的主旋律,用不同的编曲,讲述了两样之故事,不是每个音乐人还可以成功的。

“谁吃他是周杰伦。” —— 众人

较异常的大提琴 —— 打一个乐器(谜底在MV中披露)

一样闻这首歌唱之名就是懂得周杰伦以淘气了。到底什么是比老之大提琴呢,周杰伦于出的诠释是double
bass。
也不怕是低音提琴。说是最调皮的歌名,不如说是最直接的歌名。周杰伦的嗓音在收拾篇歌唱里用底是一致种植压正嗓子的俊美声音。这种唱腔从他的第一布置专辑中之《印第安底镇斑鸠》就开变异,随后的《牛仔很忙碌》,《免费教学录音带》等等都是这种作风的连续。但是趁他的秋以及年龄的滋长,嗓子中任起了同等种植沙哑。这种压在嗓门的打呼唱腔在最近底著述里更加多,包括《免费教学录音带》《天台爱情》电影原声带里的那么篇《狗仔舞》等等。好像这种唱腔唱着唱歌着便成为习惯了,这倒引来了有些老歌迷的担忧,生怕这种唱腔泛滥,影响下张新专辑的听感。有关于周杰伦唱腔的论点,在后面几段落中我会再强调。

乌克丽丽 —— “小学音乐学科候选曲目”

低音提琴喜欢吗?太可怜了?哦,那就来个mini的!哇噻!周董,又送我一个乐器?同一个专栏还来了一定量首用乐器名命名的歌。看来周董真是别有用心啊,
还为我们达成起了音乐课。想象在以后小学音乐导师教孩子等认识乐器,“同学等,上次我们询问了乌克丽丽之乐器。今天被大家介绍一栽新的乐器,它的讳称为低音提琴,它干吗被低音提琴呢?它跟一般的提琴又生出啊分别吗?我们事先来拘禁一样尽周杰伦的《比较大之大提琴》MV吧。”于是学生等看罢晚,不仅知晓了低音提琴的模样,同时还了解了它的音色。这首歌唱之长处集中在唱腔方面,时而叼着嗓门,带在头卖萌装可爱;时而压低嗓音,表现来调皮。

容易君没有差 —— 一首不略的情歌

当时是一模一样篇情歌。一首不略的情歌,听正歌时,你的心思停下中带动在压抑,夹杂的机械钟秒针走动声,让听者压抑到屏住呼吸试着感受及自己的心跳。渐进句由同样失误循环的节拍(我占
格林威看 守候着若 在时 标准起点 回忆过去 你也于 永夜了底 极地旅行
等易于 失温后
渐渐好去)由最押韵的“神”级别歌词组成,给您的感受就,这样的大循环旋律天生配这样的词。

纵然是这般同样种重复式之渐进句,引燃了通副歌的心思。如果您首先浅任就篇歌唱,副歌部分绝对难以捉摸到。这要听者产生相同栽无法解释的诡异的感触。这种感觉顿时就被自家想到了《跨时代》专辑里的《我落泪
情绪零碎》。这半篇歌唱的情义升华是无比相似之。不过,要是比较这有限篇歌唱啊一个心态还伤心,那还是一旦属《我落泪
情绪零碎》,副歌部分被您的感想就仿佛一个人给钉在老伴,房间昏暗,孤独一人数之您活动至窗户前,看正在窗户外,雨水在屋檐嘀嗒,门紧缩,却从缝里钻出一丝丝冷空气,你的平等人数吸气,把凉气吸到了心灵,引来阵阵阵酸。(看歌名就知晓,又落泪又心碎
。)

杰伦于了当时篇歌唱“抒情摇滚”的概念。这为是一律种植对自己作的挑战,首破把摇滚鼓点加入到抒情慢歌中。是上张专辑中《世界不深》的升华的作。还变说,这有限首歌唱之MV有只共同点:MV
都配上了周杰伦作主唱带领正摇滚乐队的画面,然后因叙故事之艺术成就(其自身从未涉足故事表演,只是作为一个讲述故事之别人。)

《世界不晚》MV 截图

《爱而从未差》MV 截图

当下首歌唱之词是值得称赞的,像时钟嘀嗒声这种模仿真场景的声音以周杰伦近期的著作里寥寥无几。(《自导自演》的切削喇叭声,《嘻哈空姐》中航班起飞前的提示语,接下要提到之《大笨钟》的钟声。)作为一个老歌迷,我或者老怀念《斗牛》中之篮球声(现场收音),《将军》前奏的父对话(现场收音)和《半岛铁盒》和店员对话前推进门的歌谣铃声(现场收音)。希望下周杰伦可以投入更多生活面临之声响,这样会来得编曲不过分简陋和枯燥。

大笨钟 —— “耍什么嘴硬? 耍 耍什么嘴硬?”

顿时篇歌唱杰伦用了千篇一律栽动人的唱腔(真觉得要翘着嘴才会唱得出),描述了平针对小情侣的打打闹闹的活。“耍什么嘴硬

耍什么嘴硬”这段渐进真是给你感觉到了周杰伦的俏皮,生活化的乐章(这简直就是在对话了,“耍”这个字于掌控整段歌词的节奏感,运用得特别好。)副歌部分“我无爱了您
怎么会惦记你 发什么神经 有理说不清
只是满头还非清醒”朗朗上口。虽然当时篇歌唱歌词没有呀深度,但为绝非见出多庸俗,相比叫那些“我爱尔
爱在您”歌词,我管最生活化歌词奖颁给周杰伦的《大笨钟》。

公偏头痛 —— 两年前你叫潮,葡京网上娱乐场两年后你叫俗

巨炮来哪!有些歌迷就当在巨炮与杰伦的对话的立刻一刻。周杰伦每张专辑中还见面起雷同篇歌前奏由杰伦和巨炮的对话引出。这隐约成了偶像与歌迷的同时一个许。这同时比方是乔布斯时之苹果,每个粉丝都等着发布会接近尾声必然闪出之
“one more thing”。

马上篇歌会让您预留一个印象,“公公他偏头痛 公公他偏头痛。”

整首歌节奏特别之赶快,你会听见最多的乐章就是“公公
公公公公公公公公公公公公公公”,
真的是产生种植于洗脑的感到。专辑刚发行那段时光,我还时不时听,可后来虽觉着乏味了。周杰伦以往之快歌不乏好作。可是这次公公偏头痛
没有受您认为就篇歌唱越了哪首。还记在当年发片宣传期,他再强调这是一模一样首撞潮流的歌

“宫廷风”。可是杰伦不要遗忘了,潮流是一代的,一味的追求潮流,等一代同样过,留下的哪怕是平道俗味了。不是怀有作品过了想,泛了失败,就见面发霉的。如果您本失去听第一布置专辑的《龙卷风》,你晤面看旋律过时呢?你不会见。可是《公公偏头痛》已经泛霉味了。如今自家听整张专辑,有意想过了的曲就是立首。前奏一响,就以为,怎么那么俗吗!

一年四季列车 —— 如列车一般的尽早

一年四季列车是一样篇快歌,曲子也未是颇丰富(两瓜分四十秒)真的是吃人感到一部列车在您身边嗖嗖经过,不检点就放收一全体。这篇歌唱则编曲没有复杂的元素,但是鼓点还是特别有趣的,同样一致段落旋律,同一段鼓点贯彻全曲,犹如列车在铁轨上行驶的声息。“嘟嘟”是整篇歌唱之长之一,使整首歌唱越活泼。让人口当成感叹啊,嘟嘟也克唱出来。其实,这样的唱法杰伦都不是首先涂鸦利用了,《超跑女神》中之“噢噢”也拥有类似之用法。看看以后会不见面干来什么新的?啪啪?噗噗?

傻笑 —— 男女对歌唱求过?

及时首歌唱当批发头,你会看,又拿改为平等篇经典男女对歌唱情歌。可是了会儿你见面发觉,听在听在,你就算忘记了立篇歌唱了,原因是当下首歌唱不耐听。可能是为编曲太大众,虽然孩子对歌唱部分和声配合的无所不包,但是又看格外一般。相比于珊瑚海跟屋顶,这篇歌就是站不停止哟位置了。我较欣赏的是在当时篇歌唱之后段有同样处于,声音忽然停止。这个做法有点调皮,却不突兀,让开小差的听者蒙地一想,“咦?刚才怎么了?”这种做法在《稻香》中吗发使用。算是杰伦的有点智慧吧。

手语 —— hey girl, hey girl, woo baby

立马首歌被自家留的映像很老。歌名很有噱头,手语?到底是什么品种的讴歌也?

自伊始就都被诱惑,前奏的编曲方式有点像《游园会》,先是简单的音频,然后再循环一总体加上有节奏的鼓点。类做法的歌还有《爱于西元前》等。正歌有杰伦同开口我不怕醉了,多么温暖的声调啊!

副歌部分 “我的手语你总装不亮 我好你要是比较多全方位”
给人声加入了电音处理,用法于就篇歌唱里并不为过(什么给用法用过头了,去听《迷魂曲》就懂得呀),当时听见这脑中浮想,要是当下段尚未开电音处理,嗓音可能会见显示无比尊重太干,反而不是听众想如果之。(杰伦突然膝盖刺痛,
doubling over and coughing blood.)

便于这时候,突然!突然!“fa so la si do fa fa so la si do fa”
多么难得的歌词啊!真是给我激动了一如既往将。上次将“音符”当歌词唱还是反复年前的《晴天》。作为杰伦粉丝的自家算“没道逃离你的魔法”。

“Hey Girl 牵你的右手 这不是于拉手 Hey Girl 牵你的左侧 你得想最多”
我及时笑了,这当成惜物啊!Jay有微微歌曲里发生英文歌词?到底有多难得,你用手指数数就清楚了。数在累累方公尽管发现,十到底手指还用无了,龙卷风(baby),一清;Mine
Mine (oh, mine mine),两彻底;手语(hey
girl),三干净,句号。接下来的有数篇虽然发出英文,可自就不到底进去了:(咳咳,太无算是数了。)《听妈妈的语句》(ABC),《对不起》(Jay
Chou)。

嘟着嘴唱腔也够呛讨人欣赏,在网上看别的歌迷的评介,有的人说这么的唱腔太做作了,不够自然,我反而觉得假如不是每首歌都这样歌唱,还是得承受之。每首歌唱表达的心境还未一致,不同的声调自然好渲染出不同之感情。我怀念,就算周杰伦又怎么蠢,也不见面无知到温馨的谁声音好听还划分不彻底吧。作为一个录音棚歌手(他的自称,我并未说他唱现场非常),他于录音棚里飙高音可以再唱好多全体,那句最终的周全,或许是在重重涂鸦走调,破音,或情感投入不足,不断的剪辑,重录后炼成的。这样认真的音乐人于华人乐坛还是不多之。他还早就那么红了,还以坚持不懈,还在改。

岂都是你 —— 高音警报!高潮警报!

若将要听到的凡亚洲不怎么天子之最高音。这篇歌唱前奏用了弦乐的三结合,给人一致种电影配乐的感想(不料想,这篇歌唱就是后来化了该自导自演的歌武术剧《天台爱情》的主题曲)。随后,钢琴声一起,引出正歌部分。“把其忘”,嗓音略带倒,把心梗塞的酸痛诠释了下。通过正歌的几只断句,杰伦就拿听者所有的活力聚集于了嗓子眼沙哑的边缘。“前进
找寻新的记得与空气 放晴 等雨说放弃”
。放弃的“弃”字拖长,把心思拉至了高潮边缘。终于,鼓点落下,“在发泪水的暴雨里
哪里还是公
擦干是否就是看无显现你”,一阵高潮还未终止,又迎来另一样强心剂,“脑海里
你的笑脸最干净 彻底将自己囚禁于您的透气”,紧接着,
“我随着哭泣”上扬的语调突然得到下,犹如强心针突然抽出。就于您喘息的常,你而且受填补了另外一样针剂“在产生泪水的暴风雨里
哪里还是公 抱紧而是自个儿逃离的去 太拥挤
我以你的社会风气里看不清楚的凡若要自己 我们且于相当雨住”
就这么来来回回,听者感叹十分甜美。

神作!神作!

期望启航 —— 曾经教会孩子放妈妈的口舌,如今可否叫愿意启航

自随认为写了了。于是多次了累曲的数额。咦?怎么才11首。一篇一首破查,感觉还勾了。顿时以为有了啊奇妙的作业。(上次出离奇事情的下还是那时候自己摸不顶自己之
Apple 小白耳机呢)后来自打开
iTunes,对照歌单,这才揪出单《梦想启航》,这篇歌唱到今日犹不曾为本人留十分浓的震慑。原本想打造有同样篇全新的励志歌曲的,可是,对不起,传唱度=0
。MV
为看了,大概意思为知道了,可是不管是歌词还是音频无一致处于给丁留深刻印象,我刚没听马上首歌唱,硬在头皮想想闹就首歌唱之副歌,想了平等分钟才想出去。

比方知道,励志歌曲一经是丢了传唱度,励志给何人看呢?相比《听妈妈的言辞》那段朗朗上人口的可歌旋律(就连本人有生之年的初中语文老师都能够随口说发歌词,连我妈妈还见面哼),梦想启航真的是见仁见智及十万八千里。

亲~ 喜欢吧?我知道下面的 按钮 可以随下去啊! 好神奇 喜欢一下吧↓↓↓ 有想法可以交流 XD