5

DoubleCircle_的个人空间

 3 years ago
source link: https://my.oschina.net/u/4135139/blog/4952619
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
【iOS 底层】主线程 & 主Runloop
  • 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"}
}

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK