先看结论!划重点!

首页 编程分享 EXPERIENCE 正文

anonymous 转载 编程分享 2021-02-15 00:05:06

简介 Pre macOS:Catalina 10.15.7 Xcode:12.3 objc4:objc4-787.1 基本概念 CFRunloop ...


Pre

  • macOS:Catalina 10.15.7
  • Xcode:12.3
  • objc4:objc4-787.1

基本概念

CFRunloop

  1. CFRunLoop对象监视任务的输入源,并在它们准备好进行处理时分派控制。
  2. 运行循环可以监视三种类型的对象:CFRunLoopSourceCFRunLoopTimerCFRunLoopObserver
  3. 添加到运行循环中的每个源、计时器和观察者必须与一个或多个运行循环模式相关联。
  4. Core Foundation定义了一种特殊的伪模式,称为common modes,它允许您将多个模式与给定的source、timer或observer关联起来。
  5. 每个线程只有一个运行循环。你既不创建也不销毁线程的运行循环。Core Foundation会根据需要自动为您创建它。
  6. 运行循环可以递归地运行。您可以在任何运行循环调用中调用CFRunLoopRunCFRunLoopRunInMode,并在当前线程的调用堆栈上创建嵌套的运行循环激活。
  7. Cocoa应用程序构建在CFRunLoop之上,实现它们自己的高级事件循环。在编写应用程序时,可以将源代码、计时器和观察者添加到它们的运行循环对象和模式中。然后,您的对象将作为常规应用程序事件循环的一部分被监视。使用NSRunLoop的gettcfrunloop方法可以得到对应的CFRunLoopRef类型。

NSRunloop

  • NSRunLoop是对Core Fundation中的CFRunloop的封装
  • NSRunLoop对象处理来自窗口系统的鼠标和键盘事件、NSPort对象和NSConnection对象等源的输入。NSRunLoop对象也会处理NSTimer事件。
  • 你的应用程序既不创建也不显式管理NSRunLoop对象。每个NSThread对象(包括应用程序的主线程)都有一个根据需要自动创建的NSRunLoop对象。如果需要访问当前线程的运行循环,可以使用类方法currentRunLoop来实现。
  • 注意,从NSRunLoop的角度来看,NSTimer对象不是“输入”——它们是一种特殊的类型,这意味着当它们触发时,不会导致运行循环返回。
  • NSRunLoop类通常被认为是线程不安全的,它的方法应该只在当前线程的上下文中被调用。永远不要尝试调用运行在不同线程中的NSRunLoop对象的方法,因为这样做可能会导致意想不到的结果。

线程 & Runloop

先看结论!划重点!

  • CocoaCore Foundation都提供了运行循环对象(NSRunloopCFRunloop)来帮助配置和管理线程的运行循环。
  • 应用程序不需要显式地创建这些对象;每个线程(包括主线程)都有一个关联的Runloop对象。
  • 作为应用程序启动过程的一部分,应用程序框架自动在主线程上设置并运行运行循环。而子线程需要显式地运行它们的运行循环。
  • 关系概括:App启动后,苹果在主线程创建了其关联的Runloop,并在该Runloop中注册两个Observer
    • 第一个Observer(优先级最高)监控的事件:Entry(即将进入Loop),回调内会调用_objc_autoreleasePoolPush()创建自动释放池
    • 第二个Observer(优先级最低)监控两个事件:
      • 第一个事件:BeforeWaiting(准备进入休眠),回调内会调用_objc_autoreleasePoolPop()释放旧的池并调用_objc_autoreleasePoolPush()创建新的池
      • 第二个事件:Exit(即将退出Loop),回调内会调用_objc_autoreleasePoolPop()销毁自动释放池

关系分析

  • 根据调用堆栈我们可以看出来,当调用UIApplicationMain()方法后,系统会自动为其创建相关联的Runloop
  • 通过在main()函数首行即获取主线程与主runloop可以看到:此时主线程的runloop已经存在了。我们可以推测出主线程创建后即会创建对应的runloop,也就是说,主runloop在程序一启动就默认创建好了。

    但是此时的主runloop中,还未添加相关观察者等等。
  • 当代码从main()开始执行,此时的runloop依然是一个空的结构体

  • 查看进入applicationDidLaunchingWithOptions:之前的调用堆栈

    可以看到会为此时的主Runloop添加Source等相关信息
  • 走进applicationDidLaunchingWithOptions时,再通过po CFRunloopGetMain()获取此时的主Runloop信息,可以看到观察者、source等都已添加完成
// 截取部分
current mode = kCFRunLoopDefaultMode,
common modes = <CFBasicHash 0x6000014e5260 [0x7fff8002e8c0]>{type = mutable set, count = 2,
entries =>
	0 : <CFString 0x7fff806610e0 [0x7fff8002e8c0]>{contents = "UITrackingRunLoopMode"}
	2 : <CFString 0x7fff801ab7e8 [0x7fff8002e8c0]>{contents = "kCFRunLoopDefaultMode"}
}

转载链接:https://my.oschina.net/u/4135139/blog/4952619


Tags:


本篇评论 —— 揽流光,涤眉霜,清露烈酒一口话苍茫。


    声明:参照站内规则,不文明言论将会删除,谢谢合作。


      最新评论




ABOUT ME

Blogger:袅袅牧童 | Arkin

Ido:PHP攻城狮

WeChat:nnmutong

Email:nnmutong@icloud.com

标签云