为什么我需要 NSRunLoop 来运行计时器?

oca*_*eau 3 cocoa nstimer nsrunloop

我购买了 Objective-C 的 Big Nerd Ranch Guide,但有一些NSRunLoop我想不通的地方。

这是书中的一段代码:

NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0 
                                                  target:logger
                                                selector:@selector(updateLastTime:)
                                                userInfo:nil
                                                 repeats:YES];
[[NSRunLoop currentRunLoop] run];
Run Code Online (Sandbox Code Playgroud)

我的问题是,为什么我需要为要处理NSRunLoopNSTimer对象放置一个?为什么它需要在最后,而不是开始?

为什么它不像其他函数或对象的方法那样我只需要调用一个函数来处理它并登录到控制台?

我真的很想弄清楚这里每个细节的每个逻辑。

Rob*_*ier 6

从成为 Cocoa 的早期开始,当恐龙在地球上漫游时,岩石很软,NeXT 工作站是新的,直到 10.6 出现,最常见的多任务处理类型是运行循环。这是协作式多任务处理。没有线程。没有抢占式调度程序或内核。没有上下文切换。只有一个大的运行循环说“现在需要做什么?” 并运行它。当那件事完成时,它等待下一件需要做的事情并运行它。从字面上看,这是一个大while(true)循环。好吧,从技术上讲,代码行是:

for (;;) { ... }
Run Code Online (Sandbox Code Playgroud)

您可以在CFRunLoop.c 中亲自查看。寻找__CFRunLoopRun.

NSTimer是在那个年代发明的。它所做的一切都是在 runloop 中做一个注释,告诉它“当这个时间过去时,请执行此操作”。(它比这稍微复杂一点,因为它使用 mach 端口,__CFRunLoopTimerSchedule在同一个文件中查找详细信息,但基本上就是这个想法。)

所以重点是,没有魔法。只有一个大for(;;)循环来处理这些东西。有些东西必须运行它。当您启动它时(使用run),它不会返回。这是一个无限循环。没有“背景”。没有其他线程。这就是为什么你需要按照 BNR 告诉你的顺序做事。否则您的下一行代码将无法运行。

当然,在 iOS 应用程序和 OS X GUI 应用程序中,您通常不必自己执行此操作。运行循环在程序启动期间为您创建,整个主线程都位于其中。它是大多数时候给你打电话的东西。你不叫。但是,如果您在主线程以外的线程上,并且想要使用运行循环功能,则必须自己运行它。

今天,很多事情都是用 GCD 而不是运行循环来完成的。这就是我提到的“直到 10.6 出现”。它真的改变了可可的世界。但是大量的 Cocoa 仍然依赖于运行循环,即使您从未考虑过它,它仍然是大多数应用程序的主力。

在今天的大多数情况下,如果您必须创建一个 runloop 才能使用NSTimer,那么您不应该使用NSTimer. 只需使用dispatch_after. 事实上,这就是我今天大部分时间通常推荐的内容,即使您确实有运行循环。

(你绝对应该阅读@quelish 在评论中给出的链接。它是关于运行循环的权威词。)