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

Why do I need an NSRunLoop to run a timer?

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

这是书中的一段代码:

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

我的问题是,为什么要处理NSTimer对象需要放一个NSRunLoop?为什么它需要在结尾而不是开头?

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

我真的在尝试找出这里每个细节的每个逻辑。

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

for (;;) { ... }

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

NSTimer是那个年代发明的。它所做的一切都在 运行 循环中做了一个注释,告诉它 "when this time passes, then please do this." (它比这稍微复杂一点,因为它使用 mach 端口,在同一个文件中查找 __CFRunLoopTimerSchedule 以获取详细信息,但基本上就是这个想法。)

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

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

今天,很多事情都是用 GCD 而不是 运行 循环完成的。这就是我提到的 "until 10.6 came out"。它确实改变了 Cocoa 世界。但是大量 Cocoa 仍然依赖于 运行 循环,即使您从未想过它,它仍然是大多数应用程序的主力。

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

(你绝对应该阅读评论中的 link @quelish。它是 运行 循环中的权威词。)