原文链接:http://plainboiledwaterln.cn/iOSNET/runtime.html
引导
对于从事 iOS 开发人员来说,所有的人都会答出「 Runtime 是运行时 」,什么情况下用 Runtime ?,大部分人能说出「 给分类动态添加属性 || 交换方法 」,再问一句「 Runtime 消息机制的调用流程 || 能体现 Runtime 强大之处的应用场景 」,到这,能知道答案的寥寥无几,很少有人会说到 “黑魔法” 这三个字。
Runtime 是 iOS 编程中比较难的模块,想要深入学习 OC,那 Runtime 是你必须要熟练掌握的东西,下面是我对 Runtime 的整理,从零开始,由浅入深,并且带了几个 Runtime 实际开发应用场景 –> 大神可选择性路过「思想」。
在「时间 & 知识 」有限内,总结的文章难免有「未全、不足 」的地方,还望各位好友指出,可留言指正或是补充,以提高文章质量@白开水ln原著;
目录:
- runtime 概念
- runtime 消息机制
- runtime 方法调用流程「消息机制」
- runtime 运行时常见作用
- runtime 常用开发应用场景「工作掌握」
1.runtime 交换方法
2.UITextField占位文字颜色(工具类)
3.runtime 给分类动态添加属性
4.runtime 字典转模型(Runtime 考虑三种情况实现)- runtime 运行时其它作用「面试熟悉」
1.动态添加方法
2.动态变量控制
3.实现NSCoding的自动归档和解档
4.runtime 下Class的各项操作
5.runtime 几个参数概念- 什么是 method swizzling(俗称黑魔法)
- 最后一道面试题的注解
- runtime 模块博文推荐(❤️数量较多)
- runtime & runloop 面试最常问到的题整理
【建议看】
- Demo 重要的部分代码中都有相应的注解和文字打印,运行程序可以很直观的表现。
- runtime.h
- SourceCode 、 ToolsClass、WechatPublic-Codeidea
- Runloop 模块详解「面试、工作」
- Runtime_swift3.-「面试、工作」
runtime 概念
Objective-C 是基于 C 的,它为 C 添加了面向对象的特性。它将很多静态语言在编译和链接时期做的事放到了 runtime 运行时来处理,可以说 runtime 是我们 Objective-C 幕后工作者。
-
runtime(
简称运行时
),是一套 纯C(C和汇编写的) 的API。而 OC 就是 运行时机制,也就是在运行时候的一些机制,其中最主要的是 消息机制。 -
对于 C 语言,函数的调用在编译的时候会决定调用哪个函数。
-
OC的函数调用成为消息发送,属于 动态调用过程。在编译的时候并不能决定真正调用哪个函数,只有在真正运行的时候才会根据函数的名称找到对应的函数来调用。
-
事实证明:在编译阶段,OC 可以 调用任何函数,即使这个函数并未实现,只要声明过就不会报错,只有当运行的时候才会报错,这是因为OC是运行时动态调用的。而 C 语言 调用未实现的函数 就会报错。
runtime 消息机制
我们写 OC 代码,它在运行的时候也是转换成了 runtime
方式运行的。任何方法调用本质:就是发送一个消息(用 runtime
发送消息,OC 底层实现通过 runtime
实现)。
消息机制原理:对象根据方法编号SEL去映射表查找对应的方法实现。
每一个 OC 的方法,底层必然有一个与之对应的 runtime
方法。
简单示例:
验证:方法调用,是否真的是转换为消息机制?
-
必须要导入头文件
#import <objc/message.h>
-
注解1:我们导入系统的头文件,一般用尖括号。
-
注解2:OC 解决消息机制方法提示步骤【查找
build setting
-> 搜索msg
->objc_msgSend
(YES –> NO)】 -
注解3:最终生成消息机制,编译器做的事情,最终代码,需要把当前代码重新编译,用xcode编译器,【
clang -rewrite-objc main.m
查看最终生成代码】,示例:cd main.m --> 输入前面指令,就会生成 .opp文件(C++代码)
-
注解4:这里一般不会直接导入
<objc/runtime.h>
-
- 示例代码:OC 方法–>runtime 方法
|
|
runtime 方法调用流程「消息机制」
面试:消息机制方法调用流程
- 怎么去调用
eat
方法,对象方法:(保存到类对象的方法列表) ,类方法:(保存到元类(Meta Class
)中方法列表)。- 1.OC 在向一个对象发送消息时,
runtime
库会根据对象的isa
指针找到该对象对应的类或其父类中查找方法。。 - 2.注册方法编号(这里用方法编号的好处,可以快速查找)。
- 3.根据方法编号去查找对应方法。
- 4.找到只是最终函数实现地址,根据地址去方法区调用对应函数。
- 1.OC 在向一个对象发送消息时,
- 补充:一个
objc
对象的isa
的指针指向什么?有什么作用?- 每一个对象内部都有一个isa指针,这个指针是指向它的真实类型,根据这个指针就能知道将来调用哪个类的方法。
runtime 常见作用
-
动态交换两个方法的实现
-
动态添加属性
-
实现字典转模型的自动转换
-
发送消息
-
动态添加方法
-
拦截并替换方法
-
实现 NSCoding 的自动归档和解档
runtime 常用开发应用场景「工作掌握」
runtime 交换方法
应用场景:当第三方框架 或者 系统原生方法功能不能满足我们的时候,我们可以在保持系统原有方法功能的基础上,添加额外的功能。
需求:加载一张图片直接用[UIImage imageNamed:@"image"];
是无法知道到底有没有加载成功。给系统的imageNamed
添加额外功能(是否加载图片成功)。
- 方案一:继承系统的类,重写方法.(弊端:每次使用都需要导入)
- 方案二:使用 runtime,交换方法.
实现步骤:
- 1.给系统的方法添加分类
- 2.自己实现一个带有扩展功能的方法
- 3.交换方法,只需要交换一次,
案例代码:方法+调用+打印输出
|
|
总结:我们所做的就是在方法调用流程第三步的时候,交换两个方法地址指向。而且我们改变指向要在系统的imageNamed:
方法调用前,所以将代码写在了分类的load
方法里。最后当运行的时候系统的方法就会去找我们的方法的实现。
runtime 给分类动态添加属性
原理:给一个类声明属性,其实本质就是给这个类添加关联,并不是直接把这个值的内存空间添加到类存空间。
应用场景:给系统的类添加属性的时候,可以使用runtime动态添加属性方法。
注解:系统 NSObject
添加一个分类,我们知道在分类中是不能够添加成员属性的,虽然我们用了@property
,但是仅仅会自动生成get
和set
方法的声明,并没有带下划线的属性和方法实现生成。但是我们可以通过runtime
就可以做到给它方法的实现。
需求:给系统 NSObject 类动态添加属性 name
字符串。
案例代码:方法+调用+打印
|
|
总结:其实,给属性赋值的本质,就是让属性与一个对象产生关联,所以要给NSObject
的分类的name
属性赋值就是让name
和NSObject
产生关联,而runtime
可以做到这一点。
runtime 字典转模型
字典转模型的方式:
-
一个一个的给模型属性赋值(初学者)。
-
字典转模型KVC实现
- KVC 字典转模型弊端:必须保证,模型中的属性和字典中的
key
一一对应。 - 如果不一致,就会调用
[<Status 0x7fa74b545d60> setValue:forUndefinedKey:]
报key
找不到的错。 - 分析:模型中的属性和字典的
key
不一一对应,系统就会调用setValue:forUndefinedKey:
报错。 - 解决:重写对象的
setValue:forUndefinedKey:
,把系统的方法覆盖,就能继续使用KVC,字典转模型了。
- KVC 字典转模型弊端:必须保证,模型中的属性和字典中的
-
字典转模型 Runtime 实现
-
思路:利用运行时,遍历模型中所有属性,根据模型的属性名,去字典中查找
key
,取出对应的值,给模型的属性赋值(从提醒:字典中取值,不一定要全部取出来)。 -
考虑情况:
- 1.当字典的
key
和模型的属性匹配不上。 - 2.模型中嵌套模型(模型属性是另外一个模型对象)。
- 3.数组中装着模型(模型的属性是一个数组,数组中是一个个模型对象)。
- 1.当字典的
-
注解:根据上面的三种特殊情况,先是字典的
key
和模型的属性不对应的情况。不对应有两种,一种是字典的键值大于模型属性数量,这时候我们不需要任何处理,因为runtime
是先遍历模型所有属性,再去字典中根据属性名找对应值进行赋值,多余的键值对也当然不会去看了;另外一种是模型属性数量大于字典的键值对,这时候由于属性没有对应值会被赋值为nil
,就会导致crash
,我们只需加一个判断即可。考虑三种情况下面一一注解; -
步骤:提供一个
NSObject
分类,专门字典转模型,以后所有模型都可以通过这个分类实现字典转模型。
-
-
MJExtension 字典转模型实现
- 底层也是对
runtime
的封装,才可以把一个模型中所有属性遍历出来。(你之所以看不懂,是 MJ 封装了很多层而已^_^.)。
- 底层也是对
这里针对字典转模型 KVC 实现,就不做详解了,如果你 对 KVC 详解使用或是实现原理 不是很清楚的,可以参考 实用「KVC编码 & KVO监听
字典转模型 Runtime 方式实现:
说明:下面这个示例,是考虑三种情况包含在内的转换示例,具体可以看图上的注解
1、runtime 字典转模型–>字典的 key 和模型的属性不匹配「模型属性数量大于字典键值对数」,这种情况处理如下:
|
|
注解:这里在获取模型类中的所有属性名,是采取 class_copyIvarList
先获取成员变量(以下划线开头
) ,然后再处理成员变量名->字典中的key(去掉 _ ,从第一个角标开始截取
) 得到属性名。
原因:Ivar:成员变量,以下划线开头
,Property 属性
获取类里面属性 class_copyPropertyList
获取类中的所有成员变量 class_copyIvarList
|
|
使用runtime
字典转模型获取模型属性名的时候,最好获取成员属性名Ivar
因为可能会有个属性是没有setter
和getter
方法的。
2、runtime 字典转模型–>模型中嵌套模型「模型属性是另外一个模型对象」,这种情况处理如下:
|
|
3、runtime 字典转模型–>数组中装着模型「模型的属性是一个数组,数组中是字典模型对象」,这种情况处理如下:
|
|
总结:我们既然能获取到属性类型,那就可以拦截到模型的那个数组属性,进而对数组中每个模型遍历并字典转模型,但是我们不知道数组中的模型都是什么类型,我们可以声明一个方法,该方法目的不是让其调用,而是让其实现并返回模型的类型。
这里提到的你如果不是很清楚,建议参考我的Demo,重要的部分代码中都有相应的注解和文字打印,运行程序可以很直观的表现。
runtime 其它作用「面试熟悉」
动态添加方法
应用场景:如果一个类方法非常多,加载类到内存的时候也比较耗费资源,需要给每个方法生成映射表,可以使用动态给某个类,添加方法解决。
注解:OC 中我们很习惯的会用懒加载,当用到的时候才去加载它,但是实际上只要一个类实现了某个方法,就会被加载进内存。当我们不想加载这么多方法的时候,就会使用到 runtime
动态的添加方法。
需求:runtime 动态添加方法处理调用一个未实现的方法 和 去除报错。
案例代码:方法+调用+打印输出
|
|
动态变量控制
现在有一个Person类,创建 xiaoming对象
-
动态获取 XiaoMing 类中的所有属性 [当然包括私有]
1Ivar *ivar = class_copyIvarList([ self.xiaoming class], &count); -
遍历属性找到对应name字段
1const char *varName = ivar_getName(var); -
修改对应的字段值成20
1object_setIvar( self.xiaoMing, var, @"20"); -
代码参考
1234567891011121314-( void)answer{unsigned int count = 0;Ivar *ivar = class_copyIvarList([ self.xiaoMing class], &count);for ( int i = 0; i<count; i++) {Ivar var = ivar[i];const char *varName = ivar_getName(var);NSString *name = [ NSString stringWithUTF8String:varName];if ([name isEqualToString: @"_age"]) {object_setIvar( self.xiaoMing, var, @"20");break;}}NSLog( @"XiaoMing's age is %@", self.xiaoMing.age);}
实现NSCoding的自动归档和解档
如果你实现过自定义模型数据持久化的过程,那么你也肯定明白,如果一个模型有许多个属性,那么我们需要对每个属性都实现一遍encodeObject
和 decodeObjectForKey
方法,如果这样的模型又有很多个,这还真的是一个十分麻烦的事情。下面来看看简单的实现方式。
假设现在有一个Movie
类,有3个属性。先看下 .h文件
|
|
如果是正常写法, .m 文件应该是这样的:
|
|
如果这里有100个属性,那么我们也只能把100个属性都给写一遍吗。
不过你会使用runtime
后,这里就有更简便的方法,如下。
|
|
这样的方式实现,不管有多少个属性,写这几行代码就搞定了。怎么,代码有点多,
好说下面看看更加简便的方法:两句代码搞定。
|
|
优化:上面是encodeWithCoder
和 initWithCoder
这两个方法抽成宏。我们可以把这两个宏单独放到一个文件里面,这里以后需要进行数据持久化的模型都可以直接使用这两个宏。
runtime 下Class的各项操作
下面是 runtime 下Class的常见方法 及 带有使用示例代码。各项操作,原著 http://www.jianshu.com/p/46dd81402f63
unsigned int count;
-
获取属性列表
12345objc_property_t *propertyList = class_copyPropertyList([ self class], &count);for ( unsigned int i= 0; i<count; i++) {const char *propertyName = property_getName(propertyList[i]);NSLog( @"property---->%@", [ NSString stringWithUTF8String:propertyName]);} -
获取方法列表
12345Method *methodList = class_copyMethodList([ self class], &count);for ( unsigned int i; i<count; i++) {Method method = methodList[i];NSLog( @"method---->%@", NSStringFromSelector(method_getName(method)));} -
获取成员变量列表
123456Ivar *ivarList = class_copyIvarList([ self class], &count);for ( unsigned int i; i<count; i++) {Ivar myIvar = ivarList[i];const char *ivarName = ivar_getName(myIvar);NSLog( @"Ivar---->%@", [ NSString stringWithUTF8String:ivarName]);} -
获取协议列表
123456__ unsafe_unretained Protocol **protocolList = class_copyProtocolList([ self class], &count);for ( unsigned int i; i<count; i++) {Protocol *myProtocal = protocolList[i];const char *protocolName = protocol_getName(myProtocal);NSLog( @"protocol---->%@", [ NSString stringWithUTF8String:protocolName]);}
现在有一个Person类,和person创建的xiaoming对象,有test1和test2两个方法
-
获得类方法
123Class PersonClass = object_getClass([Person class]);SEL oriSEL = @selector(test1);Method oriMethod = _class_getMethod(xiaomingClass, oriSEL); -
获得实例方法
123Class PersonClass = object_getClass([xiaoming class]);SEL oriSEL = @selector(test2);Method cusMethod = class_getInstanceMethod(xiaomingClass, oriSEL); -
添加方法
1BOOL addSucc = class_addMethod(xiaomingClass, oriSEL, method_getImplementation(cusMethod), method_getTypeEncoding(cusMethod)); -
替换原方法实现
1class_replaceMethod(toolClass, cusSEL, method_getImplementation(oriMethod), method_getTypeEncoding(oriMethod)); -
交换两个方法的实现
1method_exchangeImplementations(oriMethod, cusMethod);
常用方法
|
|
runtime 几个参数概念
以上的几种方法应该算是runtime
在实际场景中所应用的大部分的情况了,平常的编码中差不多足够用了。
这里在对 runtime
几个参数概念,做一简单说明
1、objc_msgSend
这是个最基本的用于发送消息的函数。
其实编译器会根据情况在objc_msgSend
, objc_msgSend_stret
,,objc_msgSendSuper
, 或 objc_msgSendSuper_stret
四个方法中选择一个来调用。如果消息是传递给超类,那么会调用名字带有 Super
的函数;如果消息返回值是数据结构而不是简单值时,那么会调用名字带有stret
的函数。
2、SEL
objc_msgSend
函数第二个参数类型为SEL
,它是selector
在Objc中的表示类型(Swift中是Selector类)。selector
是方法选择器,可以理解为区分方法的 ID
,而这个 ID
的数据结构是SEL
:
typedef struct objc_selector *SEL;
其实它就是个映射到方法的C字符串,你可以用 Objc 编译器命令@selector()``或者 Runtime
系统的sel_registerName
函数来获得一个SEL
类型的方法选择器。
3、id
objc_msgSend
第一个参数类型为id
,大家对它都不陌生,它是一个指向类实例的指针:
typedef struct objc_object *id;
那objc_object
又是啥呢:
struct objc_object { Class isa; };
objc_object
结构体包含一个isa
指针,根据isa
指针就可以顺藤摸瓜找到对象所属的类。
4、runtime.h里Class的定义
|
|
可以看到运行时一个类还关联了它的超类指针,类名,成员变量,方法,缓存,还有附属的协议。
在objc_class
结构体中:`ivars是
objc_ivar_list指针;
methodLists是指向
objc_method_list指针的指针。也就是说可以动态修改
*methodLists的值来添加成员方法,这也是
Category`实现的原理。
上面讲到的所有东西都在Demo里,如果你感觉这样难以理解,那强烈建议你下载Demo ,运行代码加上文字注解,效果会更好,如果你觉得不错,还请为我的Demo star一个。
什么是 method swizzling(俗称黑魔法)
-
简单说就是进行方法交换
-
在
Objective-C
中调用一个方法,其实是向一个对象发送消息,查找消息的唯一依据是selector
的名字。利用Objective-C
的动态特性,可以实现在运行时偷换selector
对应的方法实现,达到给方法挂钩的目的 -
每个类都有一个方法列表,存放着方法的名字和方法实现的映射关系,
selector
的本质其实就是方法名,IMP
有点类似函数指针,指向具体的Method
实现,通过selector
就可以找到对应的IMP
。
- 交换方法的几种实现方式
- 利用
method_exchangeImplementations
交换两个方法的实现 - 利用
class_replaceMethod
替换方法的实现 - 利用
method_setImplementation
来直接设置某个方法的IMP
。
- 利用
这里可以参考简友这篇:【Runtime Method Swizzling开发实例汇总】http://www.jianshu.com/p/f6dad8e1b848
最后一道面试题的注解
下面的代码输出什么?
|
|
先思考一下,会打印出来什么❓
关注我的更多干货分享 ^_^.
答案:都输出 Son
class
获取当前方法的调用者的类,superClass
获取当前方法的调用者的父类,super
仅仅是一个编译指示器,就是给编译器看的,不是一个指针。- 本质:只要编译器看到
super
这个标志,就会让当前对象去调用父类方法,本质还是当前对象在调用
这个题目主要是考察关于objc
中对 self
和 super
的理解:
-
self
是类的隐藏参数,指向当前调用方法的这个类的实例。而super
本质是一个编译器标示符,和self
是指向的同一个消息接受者 -
当使用
self
调用方法时,会从当前类的方法列表中开始找,如果没有,就从父类中再找; -
而当使用
super
时,则从父类的方法列表中开始找。然后调用父类的这个方法 -
调用
[self class]
时,会转化成objc_msgSend
函数
|
|
Runtime 模块博文推荐 (❤️数量较多)
作者 | Runtime 模块推荐阅读博文 |
---|---|
西木 | 完整总结 http://www.jianshu.com/p/6b905584f536 |
天口三水羊 | objc_msgSend http://www.jianshu.com/p/9e1bc8d890f9 |
夜千寻墨 | 详解 http://www.jianshu.com/p/46dd81402f63 |
袁峥Seemygo | 快速上手 http://www.jianshu.com/p/e071206103a4 |
郑钦洪_ | 实现自动化归档 http://www.jianshu.com/p/bd24c3f3cd0a |
HenryCheng | 消息机制 http://www.jianshu.com/p/f6300eb3ec3d |
卖报的小画家Sure | Method Swizzling开发实例汇总 http://www.jianshu.com/p/f6dad8e1b848 |
滕大鸟 | OC最实用的runtime总结 http://www.jianshu.com/p/ab966e8a82e2 |
黑花白花 | Runtime在实际开发中的应用 http://www.jianshu.com/p/851b21870d91 |
Runtime & Runloop 面试最常问到的题整理【建议看】
说明:此面试题针对性的摘录整理,只为方便 在面试路上准备的你 ,会注有原文。
1、整理原文:2017年5月iOS招人心得(附面试题)
Runtime
- objc在向一个对象发送消息时,发生了什么?
- 什么时候会报
unrecognized selector
错误?iOS有哪些机制来避免走到这一步? - 能否向编译后得到的类中增加实例变量?能否向运行时创建的类中添加实例变量?为什么?
- runtime如何实现
weak
变量的自动置nil? - 给类添加一个属性后,在类结构体里哪些元素会发生变化?
RunLoop
- runloop是来做什么的?runloop和线程有什么关系?主线程默认开启了runloop么?子线程呢?
- runloop的
mode
是用来做什么的?有几种mode
? - 为什么把NSTimer对象以
NSDefaultRunLoopMode(kCFRunLoopDefaultMode)
添加到主运行循环以后,滑动scrollview
的时候NSTimer
却不动了? - 苹果是如何实现
Autorelease Pool
的?
//——————– 【我是分割线】 ———————//
整理原文:2017年iOS面试题总结,附上答案
Runtime
01 |
---|
问题: objc在向一个对象发送消息时,发生了什么? |
解答: 根据对象的 isa 指针找到类对象 id,在查询类对象里面的 methodLists 方法函数列表,如果没有在好到,在沿着 superClass ,寻找父类,再在父类 methodLists 方法列表里面查询,最终找到 SEL ,根据 id 和 SEL 确认 IMP(指针函数),在发送消息; |
03 |
---|
问题: 什么时候会报unrecognized selector错误?iOS有哪些机制来避免走到这一步? |
解答: 当发送消息的时候,我们会根据类里面的 methodLists 列表去查询我们要动用的SEL,当查询不到的时候,我们会一直沿着父类查询,当最终查询不到的时候我们会报 unrecognized selector 错误,当系统查询不到方法的时候,会调用 +(BOOL)resolveInstanceMethod:(SEL)sel 动态解释的方法来给我一次机会来添加,调用不到的方法。或者我们可以再次使用 -(id)forwardingTargetForSelector:(SEL)aSelector 重定向的方法来告诉系统,该调用什么方法,一来保证不会崩溃。 |
04 |
---|
问题: 能否向编译后得到的类中增加实例变量?能否向运行时创建的类中添加实例变量?为什么? |
解答: 1、不能向编译后得到的类增加实例变量 2、能向运行时创建的类中添加实例变量。【解释】:1. 编译后的类已经注册在 runtime 中,类结构体中的 objc_ivar_list 实例变量的链表和 instance_size 实例变量的内存大小已经确定,runtime会调用 class_setvarlayout 或 class_setWeaklvarLayout 来处理strong weak 引用.所以不能向存在的类中添加实例变量。2. 运行时创建的类是可以添加实例变量,调用class_addIvar函数. 但是的在调用 objc_allocateClassPair 之后,objc_registerClassPair 之前,原因同上. |
05 |
---|
问题: runtime如何实现weak变量的自动置nil? |
解答: runtime 对注册的类, 会进行布局,对于 weak 对象会放入一个 hash 表中。 用 weak 指向的对象内存地址作为 key,当此对象的引用计数为0的时候会 dealloc,假如 weak 指向的对象内存地址是a,那么就会以a为键, 在这个 weak 表中搜索,找到所有以a为键的 weak 对象,从而设置为 nil。 |
06 |
---|
问题: 给类添加一个属性后,在类结构体里哪些元素会发生变化? |
解答: instance_size :实例的内存大小;objc_ivar_list *ivars:属性列表 |
RunLoop
01 |
---|
问题: runloop是来做什么的?runloop和线程有什么关系?主线程默认开启了runloop么?子线程呢? |
解答: runloop: 从字面意思看:运行循环、跑圈,其实它内部就是do-while循环,在这个循环内部不断地处理各种任务(比如Source、Timer、Observer)事件。runloop和线程的关系:一个线程对应一个RunLoop,主线程的RunLoop默认创建并启动,子线程的RunLoop需手动创建且手动启动(调用run方法)。RunLoop只能选择一个Mode启动,如果当前Mode中没有任何Source(Sources0、Sources1)、Timer,那么就直接退出RunLoop。 |
02 |
---|
问题: runloop的mode是用来做什么的?有几种mode? |
解答: model:是runloop里面的运行模式,不同的模式下的runloop处理的事件和消息有一定的差别。系统默认注册了5个Mode:(1)kCFRunLoopDefaultMode: App的默认 Mode,通常主线程是在这个 Mode 下运行的。(2)UITrackingRunLoopMode: 界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响。(3)UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用。(4)GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到。(5)kCFRunLoopCommonModes: 这是一个占位的 Mode,没有实际作用。注意iOS 对以上5中model进行了封装 NSDefaultRunLoopMode、NSRunLoopCommonModes |
03 |
---|
问题: 为什么把NSTimer对象以NSDefaultRunLoopMode(kCFRunLoopDefaultMode)添加到主运行循环以后,滑动scrollview的时候NSTimer却不动了? |
解答: nstime对象是在 NSDefaultRunLoopMode下面调用消息的,但是当我们滑动scrollview的时候,NSDefaultRunLoopMode模式就自动切换到UITrackingRunLoopMode模式下面,却不可以继续响应nstime发送的消息。所以如果想在滑动scrollview的情况下面还调用nstime的消息,我们可以把nsrunloop的模式更改为NSRunLoopCommonModes. |
04 |
---|
问题: 苹果是如何实现Autorelease Pool的? |
解答: Autorelease Pool作用:缓存池,可以避免我们经常写relase的一种方式。其实就是延迟release,将创建的对象,添加到最近的autoreleasePool中,等到autoreleasePool作用域结束的时候,会将里面所有的对象的引用计数器 - autorelease. |
后续遇到针对 runtime&runloop常面相关,会及时在这里补充;
效果图
Reading
-
如果在阅读过程中遇到 error || new ideas,希望你能 issue 我,我会及时补充谢谢。
-
不管谁的博客上面写的 (也包括自己),阅读的你要敢于去验证;如人饮水,冷暖自知;(共勉)。
-
喜欢可 赞赏 or Star一波;点击左上角关注 或 微众『Codeidea』,在 Demo 更新时收到邮件通知,便捷你的阅读。