runtime常见问题

1 . iOS runtime 运行时,动态添加属性方法
首先, 要明白为什么要动态给类添加方法?

如果一个类方法很多,加载类到内存的时候也比较耗费资源,需要给每个方法生成一个映射表,可以使用动态给某个类添加方法。也就是说:"不去实现类的方法,采用动态的方式添加,通过不提前加入内存的方式来减少消耗"

    ivar表示成员变量
    class_addIvar
    class_addMethod
    class_addProperty
    class_addProtocol
    class_replaceProperty

如果我们需要动态的加载一个属性,我们就要使用这个set方法首先我们分析一下,OBJC_EXPORT void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
参数含义
• id object : 源对象 -指定我们需要绑定的对象,e.g ,给一个字符串添加一个内容
• const void * key : 设置一个静态常亮,也就是Key 值,通过这个我们可以找到我们关联对象的那个数据值
• id value 这个是我们打点调用属性的时候会自动调用set方法进行传值
• objc_AssociationPolicy policy : 这个是关联策略,这几个管理策略,我们看下都有什么;

typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
    
    // 第一个关联策略是基于基本类型的,也就是我们常用的assign 属性
    OBJC_ASSOCIATION_ASSIGN = 0,           /**< Specifies a weak reference to the associated object. */
    //关联策略是基于对象类型的,如我们正常的是retain nonatomic (非原子操作)类型 ,retain  : 保留一个引用指针,内存地址不修改,指向同一块内存地址
    OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, /**< Specifies a strong reference to the associated object.
                                            *   The association is not made atomically. */
    //这个相当于属性中对一些对象或者字符串进行的copy 这个是拷贝一个新对象,内存地址不在一起,还是使用的非原子类型,非原子类型我们也称之为线程不安全的操作,但是对于OC里面的数据操作,我们尽量避开原子操作,原子操作是线程安全的,会影响我们对数据的写入操作
    OBJC_ASSOCIATION_COPY_NONATOMIC = 3,   /**< Specifies that the associated object is copied.
                                            *   The association is not made atomically. */
    // 简单的指针引用, retain 操作
    OBJC_ASSOCIATION_RETAIN = 01401,       /**< Specifies a strong reference to the associated object.
                                            *   The association is made atomically. */
    //把简单的对象拷贝到一个新的内存地址
    OBJC_ASSOCIATION_COPY = 01403          /**< Specifies that the associated object is copied.
                                            *   The association is made atomically. */
};

以上就是我们动态添加属性索要用到的一些方法,简单理解就是
: set(源对象,常亮key,数值,属性关联策略)

下面这个想必都可以看明白,类似我们的getter 方法

__OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_1);```

同样 传入的参数有两个:

    id object :当前的属性是关联在那个对象上的,和set 保持一致
    const void * key : 静态关键key 要获取的值就是根据关联对象可以关键key 获取对应的数据

使用方法我们介绍完毕了,下面直接代码:
UIControl+RYButton.h 

import <UIKit/UIKit.h>

@interface UIControl (RYButton)
// 声明一个时间间隔
@property (assign,nonatomic)NSTimeInterval ry_time;

@end

UIControl+RYButton.m 

import "UIControl+RYButton.h"

import <objc/runtime.h>

static const char * RY_CLICKKEY = "ry_clickkey";
@implementation UIControl (RYButton)

  • (void)setRy_time:(NSTimeInterval)ry_time{
    objc_setAssociatedObject(self, RY_CLICKKEY, @(ry_time), OBJC_ASSOCIATION_ASSIGN);

}

  • (NSTimeInterval)ry_time{
    return [objc_getAssociatedObject(self, RY_CLICKKEY) doubleValue];

}
@end

使用方法
  • (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    ThirdPartService * service = [ThirdPartService new];
    NSLog(@"%@ ---%@",NSStringFromSelector(_cmd),service);

    UIButton * button = [UIButton buttonWithType:UIButtonTypeCustom];
    button.ry_time = 1.0f;

}

2 . runtime 如何实现 weak 属性

>首先要搞清楚weak属性的特点
weak策略表明该属性定义了一种“非拥有关系” (nonowning relationship)。为这种属性设置新值时,设置方法既不保留新值,也不释放旧值。此特质同assign类似;然而在属性所指的对象遭到摧毁时,属性值也会清空(nil out)

>那么runtime如何实现weak变量的自动置nil?

>runtime对注册的类,会进行布局,会将 weak 对象放入一个 hash 表中。用 weak 指向的对象内存地址作为 key,当此对象的引用计数为0的时候会调用对象的 dealloc 方法,假设 weak 指向的对象内存地址是a,那么就会以a为key,在这个 weak hash表中搜索,找到所有以a为key的 weak 对象,从而设置为 nil。

>weak属性需要在dealloc中置nil么
在ARC环境无论是强指针还是弱指针都无需在 dealloc 设置为 nil , ARC 会自动帮我们处理
即便是编译器不帮我们做这些,weak也不需要在dealloc中置nil
在属性所指的对象遭到摧毁时,属性值也会清空

>```objc// 模拟下weak的setter方法,大致如下- (void)setObject:(NSObject *)object{ objc_setAssociatedObject(self, "object", object, OBJC_ASSOCIATION_ASSIGN); [object cyl_runAtDealloc:^{ _object = nil; }];}```

3 . runtime如何通过selector找到对应的IMP地址?(分别考虑类方法和实例方法)
>每一个类对象中都一个对象方法列表(对象方法缓存)
类方法列表是存放在类对象中isa指针指向的元类对象中(类方法缓存)
方法列表中每个方法结构体中记录着方法的名称,方法实现,以及参数类型,其实selector本质就是方法名称,通过这个方法名称就可以在方法列表中找到对应的方法实现.
当我们发送一个消息给一个NSObject对象时,这条消息会在对象的类对象方法列表里查找
当我们发送一个消息给一个类时,这条消息会在类的Meta Class对象的方法列表里查找。

4 . 使用runtime Associate方法关联的对象,需要在主对象dealloc的时候释放么?

>无论在MRC下还是ARC下均不需要被关联的对象在生命周期内要比对象本身释放的晚很多,它们会在被 NSObject -dealloc 调用的object_dispose()方法中释放**补充:对象的内存销毁时间表,分四个步骤**

1、调用 -release :引用计数变为零

  • 对象正在被销毁,生命周期即将结束.
  • 不能再有新的 __weak 弱引用,否则将指向 nil.
  • 调用 [self dealloc]

2、 父类调用 -dealloc

  • 继承关系中最直接继承的父类再调用 -dealloc
  • 如果是 MRC 代码 则会手动释放实例变量们(iVars)
  • 继承关系中每一层的父类 都再调用 -dealloc

3、NSObject 调 -dealloc

  • 只做一件事:调用 Objective-C runtime 中object_dispose() 方法
  1. 调用 object_dispose()
  • 为 C++ 的实例变量们(iVars)调用 destructors
  • 为 ARC 状态下的 实例变量们(iVars) 调用 -release
  • 解除所有使用 runtime Associate方法关联的对象
  • 解除所有 __weak 引用
  • 调用 free()
5 . _objc_msgForward函数是做什么的?直接调用它将会发生什么?
>_objc_msgForward是IMP类型,用于消息转发的:
当向一个对象发送一条消息,但它并没有实现的时候,_objc_msgForward会尝试做消息转发直接调用_objc_msgForward是非常危险的事,
这是把双刃刀,如果用不好会直接导致程序Crash,但是如果用得好,能做很多非常酷的事
[JSPatch](https://github.com/bang590/JSPatch)就是直接调用_objc_msgForward来实现其核心功能的
[详细解说](https://github.com/ChenYilong/iOSInterviewQuestions/blob/master/01%E3%80%8A%E6%8B%9B%E8%81%98%E4%B8%80%E4%B8%AA%E9%9D%A0%E8%B0%B1%E7%9A%84iOS%E3%80%8B%E9%9D%A2%E8%AF%95%E9%A2%98%E5%8F%82%E8%80%83%E7%AD%94%E6%A1%88/%E3%80%8A%E6%8B%9B%E8%81%98%E4%B8%80%E4%B8%AA%E9%9D%A0%E8%B0%B1%E7%9A%84iOS%E3%80%8B%E9%9D%A2%E8%AF%95%E9%A2%98%E5%8F%82%E8%80%83%E7%AD%94%E6%A1%88%EF%BC%88%E4%B8%8B%EF%BC%89.md)参见这里的第一个问题解答文

6 . 能否向编译后得到的类中增加实例变量?能否向运行时创建的类中添加实例变量?为什么?
> · 不能向编译后得到的类中增加实例变量;
· 能向运行时创建的类中添加实例变量;
分析如下:
>>1. 因为编译后的类已经注册在runtime中,类结构体中的objc_ivar_list 实例变量的链表和instance_size实例变量的内存大小已经确定,同时runtime 会调用class_setIvarLayout 或 class_setWeakIvarLayout来处理strong weak引用,所以不能向存在的类中添加实例变量
>>2. 运行时创建的类是可以添加实例变量,调用 class_addIvar函数,但是得在调用objc_allocateClassPair之后,objc_registerClassPair之前,原因同上。

7 . 简述下Objective-C中调用方法的过程(runtime)
>Objective-C是动态语言,每个方法在运行时会被动态转为消息发送,即:objc_msgSend(receiver, selector),整个过程介绍如下:

>>1. objc在向一个对象发送消息时,runtime库会根据对象的isa指针找到该对象实际所属的类
2. 然后在该类中的方法列表以及其父类方法列表中寻找方法运行
3. 如果,在最顶层的父类(一般也就NSObject)中依然找不到相应的方法时,程序在运行时会挂掉并抛出异常unrecognized selector sent to XXX
4. 但是在这之前,objc的运行时会给出三次拯救程序崩溃的机会,这三次拯救程序奔溃的说明见问题[《什么时候会报unrecognized selector的异常》](http://www.jianshu.com/p/0ad2f1a9386d)中的说明

>补充说明:Runtime 铸就了Objective-C 是动态语言的特性,使得C语言具备了面向对象的特性,在程序运行期创建,检查,修改类、对象及其对应的方法,这些操作都可以使用runtime中的对应方法实现。

8 . 什么是method swizzling(俗称黑魔法)
>1. 简单说就是进行方法交换
2. 在Objective-C中调用一个方法,其实是向一个对象发送消息,查找消息的唯一依据是selector的名字。利用Objective-C的动态特性,可以实现在运行时偷换selector对应的方法实现,达到给方法挂钩的目的
3. 每个类都有一个方法列表,存放着方法的名字和方法实现的映射关系,selector的本质其实就是方法名,IMP有点类似函数指针,指向具体的Method实现,通过selector就可以找到对应的IMP 
![](http://upload-images.jianshu.io/upload_images/2790607-5e35e961f6e6521f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
4. 交换方法的几种实现方式
   a. 利用 method_exchangeImplementations 交换两个方法的实现
    b .利用 class_replaceMethod 替换方法的实现
    c. 利用 method_setImplementation 来直接设置某个方法的IMP 
![](http://upload-images.jianshu.io/upload_images/2790607-799f646c89ed1d2c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


9. 消息机制中, 对象如何找到对应的方法去调用

>// 方法保存到什么地方?对象方法保存到类中,类方法保存到元类(meta class),每一个类都有方法列表methodList
//明确去哪个类中调用,通过isa指针
    1.根据对象的isa去对应的类查找方法,isa:判断去哪个类查找对应的方法 指向方法调用的类
    2.根据传入的方法编号SEL,里面有个哈希列表,在列表中找到对应方法Method(方法名)
    3.根据方法名(函数入口)找到函数实现,函数实现在方法区

**注: 该博文并非原创, 但原文链接不可查, 若原文作者看到, 望留下原文链接, 谢谢. **
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352

推荐阅读更多精彩内容

  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    40c0490e5268阅读 1,703评论 0 9
  • 这篇文章完全是基于南峰子老师博客的转载 这篇文章完全是基于南峰子老师博客的转载 这篇文章完全是基于南峰子老师博客的...
    西木阅读 30,551评论 33 466
  • 在这个信息爆炸的时代,在这个离了手机都不知道手应该放哪的时代,在这个低头族随处可见的时代,朋友圈、点赞、回复...
    小小东阅读 631评论 0 0
  • 战城南,死郭北,野死不葬乌可食。 为我谓乌:且为客豪! 野死谅不葬,腐肉安能去子逃? …… 马车奔跑在雪地里,歌声...
    菁瓜阅读 326评论 0 1
  • 今天我们跳兔子舞,我今天跳兔子舞的时候,我胸前有点疼。我坚持跳完兔子舞,没说给老师,因为我是男子汉,我必须要坚强。
    张广远一班阅读 241评论 0 0