1.runtime介绍(https://www.jianshu.com/p/ea2d0a6fa8d6)
OC是一门动态语言,所以它总想办法把一些决定工作从编译推迟到运行时。也就是说只有编译器是不够的,它还需要一个运行时系统来执行编译后的代码。这就是Runtime系统存在的意义,它是整个OC的一个基石。
Runtime基本是用C和汇编语言写的,可见苹果为动态系统的高效做出的努力。
Runtime库主要做下面几件事:
封装:在这个库中,对象可以用C语言中的结构体表示,而方法可以用C函数来实现,另外再加上了一些额外的特性。这些结构体和函数被runtime函数封装后,我们就可以在程序运行时创建,检查,修改类、对象和它们的方法了。
runtime核心是消息传递;
1. 消息发送流程
确定调用方法的类是否加载完毕
调用objc_msgSend方法,给调用对象发送消息
对象在方法缓存列表(cache)中查询是否有对应方法,有直接调用
没有的话在对象方法列表中查找->父类->>>nsobject 没找到进入消息动态解析
2. 消息动态解析
接收消息对象未找到对应的实现方法,调用solverStanceMethod或者solverClassMethod 看是否进行方法动态创建方法 创建了就直接使用再次进入消息发送流程没有就进入消息转发
接收消息对象未找到对应的实现方法,调用solverStanceMethod后者solverClassMethod看是否进行方法动态创建方法 创建了就直接使用再次进入
3. 消息转发
快速转发:forwardingTargertSelector方法进行方法执行对象重定向这个快速转发方法,没有处理就进入完整消息转发 调用methodSignatureForSelector获得函数的参数和返回值类型 创建nsinvocation(求助对象) 发送forwardingInvocation消息给目标对象 没获取到函数信息 就会发出 donsNotrecognizeselector消息 崩溃
应用
1. 动态方法交换 method swizzling
通过 class_getClassMethod()/Class_getInStanceMethod()方法,获取方法实现地址,通过method_exhangeImplementtations()方法交换两个方法
方法的置换在类的 +load方法中调用
拦截并替换系统方法
2. 分类添加新属性
借助runtime的关联对象特性,帮助我们在运行阶段任意属性关联到一个对象上,实现属性一样的效果;
例子: nsdate分类的year/month/day/hour等
3. 获取类的详细信息
包裹属性列表/成员变量/方法/协议等
4. 动态添加方法和修改属性变量的值
5. 归档和解档 字典和模型的转换(KVC)
6. KVO实现原理
利用Runtime生成一个中间对象,让原对象的isa指针指向它,然后重写 setter方法,插入willChangeValueForKey和didChangeValueForKey方 法。 当属 性变化时会调用,会调用这两个方法通知到外界属性变化。
runloop
它是一个处理事件的循环(线程进入这个循环,运行事件处理程序来响应传入的事件),RunLoop的目的是当有事件需要处理时,线程是活跃的、忙碌的,当没有事件后,线程进入休眠
Runloop Mode实际上是 Source,Timer 和 Observer 的集合,不同的 Mode 把不同组的Source,Timer和Observer隔绝开来.Runloop 在某个时刻只能跑在一个 Mode 下,处理这一个 Mode 当中的 Source,Timer 和 Observer
基本作用
1、保持程序的持续运行
2、处理App中的各种事件(比如触摸事件、定时器事件等)
3、节省CPU资源,提高程序性能:该做事时做事,该休息时休息
应用范畴
1、定时器(Timer)、PerformSelector
2、GCD Async Main Queue
3、事件响应、手势识别、界面刷新
4、网络请求
5、AutoreleasePool
runloop与线程之间的关系
1、每条线程都有唯一的一个与之对应的RunLoop对象
2、RunLoop保存在一个全局的Dictionary里,线程作为key,RunLoop作为value
3、线程刚创建时并没有RunLoop对象,RunLoop会在第一次获取它时创建([NSRunLoop currentRunLoop])
4、RunLoop会在线程结束时销毁
5、主线程的RunLoop已经自动获取(创建),子线程默认没有开启RunLoop
runloop内部实现逻辑
第一步:首先通知Observers进入Loop 然后处理一些 定时器、事件、block
第二步:事件处理完成之后通知Observers进入休眠状态开始休眠 等待消息唤醒
第三步:通知Observers结束休眠处理一些 定时器、事件、block
autoreleasePool 在何时被释放
App启动后,苹果在主线程 RunLoop 里注册了两个 Observer,其回调都是 _wrapRunLoopWithAutoreleasePoolHandler()
第一个 Observer 监视的事件是 Entry(即将进入Loop),其回调内会调用 _objc_autoreleasePoolPush() 创建自动释放池,优先级最高,保证创建释放池发生在其他所有回调之前
第二个 Observer 监视了两个事件: BeforeWaiting(准备进入休眠) 时调用_objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 释放旧的池并创建新池;Exit(即将退出Loop) 时调用 _objc_autoreleasePoolPop() 来释放自动释放池,优先级最低,保证其释放池子发生在其他所有回调之后
在主线程执行的代码,通常是写在诸如事件回调、Timer回调内的。这些回调会被 RunLoop 创建好的 AutoreleasePool 环绕着,所以不会出现内存泄漏,开发者也不必显示创建 Pool 了
PerformSelector 的实现原理
当调用NSObject的performSelecter:afterDelay:后,实际上其内部会创建一个Timer并添加到当前线程的RunLoop中,所以如果当前线程没有RunLoop,则这个方法会失效