ios内存管理(五):RunLoop与AutoreleasePool

  RunLoop在ios开发中,是一个非常基础又非常核心的概念,但是由于比较偏底层,所以不是特别容易理解。不过好在其代码是开源的(CFRunLoopRef的源代码在Apple Open Source里面可以找到,类似其他情况NSRunloop是CFRunLoopRef的面向对象封装),相关的文章又非常多,只要多花点时间总能理解清楚。本文接上文,主要从其和AutorelasePool的关系出发,粗略写一下我对Runloop的理解,不会深入写Runloop的细节。

  Runloop到底是什么?首先,ios是一个事件驱动的操作系统,简单来看整个生命周期就是初始化、接收消息,处理消息,整个系统由许许多多个消息处理单元组成;其次,cocoa框架是一个面向对象的框架,面向对象非常重要的一个思想就是对一个过程的封装。所以Runloop其实就是对一个基础事件驱动功能的面向对象封装。它管理了响应的事件类型、响应的过程,处理方式,给外界提供了查询其状态的方法,以及状态改变时告知外界。
  好吧,略去所有细节,Runloop就是一个死循环!当执行run方法后,循环启动,有事件过来就进行处理,否则就休眠(以非常低的资源消耗维持这个死状态)。

  那么,一个应用也就是一个进程,可以有几个Runloop?谁来启动,谁来销毁Runloop呢?
  尝试创建一个NSRunloop对象,看看它的结构:

NSRunLoop *runloop = [[NSRunLoop alloc] init];
[runloop run];

  打断点观察runloop的结构发现其为nil。这说明我们不能创建NSRunloop对象。查看NSRunLoop.h文件(Foundation框架中),可以看到有两个属性来供框架使用者获取RunLoop对象:

@property (class, readonly, strong) NSRunLoop *currentRunLoop;
@property (class, readonly, strong) NSRunLoop *mainRunLoop; // 略去API_AVAILABLE

  因为NSRunLoop是对CFRunLoopRef的封装,我们可以在开源代码中查到这两个C函数:CFRunLoopGetMain() 和 CFRunLoopGetCurrent(),对应于这两个属性的getter方法:

CFRunLoopRef CFRunLoopGetMain() {
    return _CFRunLoopGet(pthread_main_thread_np());
}

CFRunLoopRef CFRunLoopGetCurrent() {
    return _CFRunLoopGet(pthread_self());
}

  可以看到是通过_CFRunLoopGet这个C函数获取到的。输入的参数不出所料,mainRunLoop是主线程,currentRunLoop是当前线程。这说明RunLoop跟线程是息息相关的。实际上看_CFRunLoopGet函数的源代码就会发现(这里不贴了),RunLoop和线程是一一对应的。必须先有线程,在使用该线程的RunLoop的第一次才完成其初始化,线程销毁时,RunLoop也随之销毁。
  所以我觉得,在ios里,RunLoop是一个依附于线程的、为线程提供持续接收并处理某些类型消息的能力的一个对象。否则没有RunLoop的话,线程执行完毕后既会退出,无法驻留在后台。

  回到RunLoop与AutoreleasePool的关系,上节说了AutoreleasePool的机制,但没有说明具体的创建时机已经drain方法的调用时机,本节就可以给出答案了。RunLoop可以将自己的状态变化告知外界,具体是通过观察者模式实现的,状态变化后,观察者会收到通知,执行相应的代码。
  在程序运行时打断点,可以通过po [NSRunLoop mainRunLoop]查看mainRunLoop的观察者,搜索autorelease可以找到相关的信息。App启动后,苹果在主线程 RunLoop 里注册了两个 Observer,其回调都是 _wrapRunLoopWithAutoreleasePoolHandler()。第一个 Observer 监视的事件是 Entry(即将进入Loop),其回调内会调用 _objc_autoreleasePoolPush() 创建自动释放池。其 order 是-2147483647,优先级最高,保证创建释放池发生在其他所有回调之前。第二个 Observer 监视了两个事件: BeforeWaiting(准备进入休眠) 时调用_objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 释放旧的池并创建新池;Exit(即将退出Loop) 时调用 _objc_autoreleasePoolPop() 来释放自动释放池。这个 Observer 的 order 是 2147483647,优先级最低,保证其释放池子发生在其他所有回调之后。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • runtime 和 runloop 作为一个程序员进阶是必须的,也是非常重要的, 在面试过程中是经常会被问到的, ...
    SOI阅读 21,897评论 3 63
  • Runloop是iOS和OSX开发中非常基础的一个概念,从概念开始学习。 RunLoop的概念 -般说,一个线程一...
    小猫仔阅读 1,038评论 0 1
  • 转载:http://www.cocoachina.com/ios/20150601/11970.html RunL...
    Gatling阅读 1,482评论 0 13
  • RunLoop 的概念 一般来讲,一个线程一次只能执行一个任务,执行完成后线程就会退出。如果我们需要一个机制,让线...
    Mirsiter_魏阅读 636评论 0 2
  • 国外教育和国内教育的区别,这个问题比较宽泛,我们应试,国外素质这样的回答,我就不做详细的解答,我从其他方面来简单回...
    发光的金子2017阅读 520评论 0 2