120

谁能解释一下是什么NSRunLoop?所以据我所知NSRunLoop,与NSThread权利有关吗?所以假设我创建了一个像

NSThread* th=[[NSThread alloc] initWithTarget:self selector:@selector(someMethod) object:nil];
[th start];

-(void) someMethod
{
    NSLog(@"operation");
}

那么在这个线程完成他的工作之后呢?为什么使用RunLoops或在哪里使用?从Apple docs我读过一些东西,但对我来说不清楚,所以请尽可能简单地解释

4

5 回答 5

224

A run loop is an abstraction that (among other things) provides a mechanism to handle system input sources (sockets, ports, files, keyboard, mouse, timers, etc).

Each NSThread has its own run loop, which can be accessed via the currentRunLoop method.

In general, you do not need to access the run loop directly, though there are some (networking) components that may allow you to specify which run loop they will use for I/O processing.

A run loop for a given thread will wait until one or more of its input sources has some data or event, then fire the appropriate input handler(s) to process each input source that is "ready.".

After doing so, it will then return to its loop, processing input from various sources, and "sleeping" if there is no work to do.

That's a pretty high level description (trying to avoid too many details).

EDIT

An attempt to address the comment. I broke it into pieces.

  • it means that i can only access/run to run loop inside the thread right?

Indeed. NSRunLoop is not thread safe, and should only be accessed from the context of the thread that is running the loop.

  • is there any simple example how to add event to run loop?

If you want to monitor a port, you would just add that port to the run loop, and then the run loop would watch that port for activity.

- (void)addPort:(NSPort *)aPort forMode:(NSString *)mode

You can also add a timer explicitly with

- (void)addTimer:(NSTimer *)aTimer forMode:(NSString *)mode
  • what means it will then return to its loop?

The run loop will process all ready events each iteration (according to its mode). You will need to look at the documentation to discover about run modes, as that's a bit beyond the scope of a general answer.

  • is run loop inactive when i start the thread?

In most applications, the main run loop will run automatically. However, you are responsible for starting the run loop and responding to incoming events for threads you spin.

  • is it possible to add some events to Thread run loop outside the thread?

I am not sure what you mean here. You don't add events to the run loop. You add input sources and timer sources (from the thread that owns the run loop). The run loop then watches them for activity. You can, of course, provide data input from other threads and processes, but input will be processed by the run loop that is monitoring those sources on the thread that is running the run loop.

  • does it mean that sometimes i can use run loop to block thread for a time

Indeed. In fact, a run loop will "stay" in an event handler until that event handler has returned. You can see this in any app simply enough. Install a handler for any IO action (e.g., button press) that sleeps. You will block the main run loop (and the whole UI) until that method completes.

The same applies to any run loop.

I suggest you read the following documentation on run loops:

https://developer.apple.com/documentation/foundation/nsrunloop

and how they are used within threads:

https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html#//apple_ref/doc/uid/10000057i-CH16-SW1

于 2012-08-23T13:00:33.323 回答
16

运行循环 交互式应用程序与 命令行工具区分开来。

  • 命令行工具使用参数启动,执行它们的命令,然后退出。
  • 交互式应用程序等待用户输入、做出反应,然后继续等待。

这里

它们允许你等到用户点击并做出相应的响应,等到你得到一个completionHandler并应用它的结果,等到你得到一个计时器并执行一个功能。如果你没有运行循环,那么你不能监听/等待用户点击,你不能等到网络调用发生,你不能在 x 分钟内被唤醒,除非你使用DispatchSourceTimerDispatchWorkItem

同样来自此评论

后台线程没有自己的运行循环,但您可以添加一个。例如, AFNetworking 2.x做到了。对于后台线程上的 NSURLConnection 或 NSTimer 来说,这是经过验证的真正技术,但我们自己不再这样做了,因为更新的 API 消除了这样做的需要。但似乎 URLSession 确实如此,例如,这里是简单的请求,在主队列上运行 [参见图像的左侧面板] 完成处理程序,您可以看到它在后台线程上有一个运行循环


具体来说:“后台线程没有自己的运行循环”。以下计时器无法为异步调度触发:

class T {
    var timer: Timer?

    func fireWithoutAnyQueue() {
        timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: false, block: { _ in
            print("without any queue") // success. It's being ran on main thread, since playgrounds begin running from main thread
        })
    }

    func fireFromQueueAsnyc() {
        let queue = DispatchQueue(label: "whatever")
        queue.async {
            self.timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: false, block: { (_) in
                print("from a queue — async") // failed to print
            })
        }
    }

    func fireFromQueueSnyc() {
        let queue = DispatchQueue(label: "whatever")
        queue.sync {
            timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: false, block: { (_) in
                print("from a queue — sync") // success. Weird. Read my possible explanation below
            })
        }
    }

    func fireFromMain() {
        DispatchQueue.main.async {
            self.timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: false, block: { (_) in
                print("from main queue — sync") //success
            })
        }
    }
}

我认为该sync块也运行的原因是:

同步块通常只是从其队列中执行。在这个例子中,源队列是主队列,无论是什么队列都是目标队列。

为了测试我RunLoop.current在每个调度中都登录了。

同步调度与主队列具有相同的运行循环。而异步块中的 RunLoop 是与其他实例不同的实例。你可能会想为什么会RunLoop.current返回不同的值。不就是共同的价值吗!?好问题!进一步阅读:

重要的提示:

属性 current不是全局变量。

返回当前线程的运行循环。

这是上下文的。它仅在线程范围内可见,即Thread-local storage。有关更多信息,请参见此处

这是计时器的一个已知问题。如果你使用,你没有同样的问题DispatchSourceTimer

于 2017-08-04T20:25:09.953 回答
8

RunLoops 有点像一个刚刚发生事情的盒子。

基本上,在一个 RunLoop 中,你去处理一些事件然后返回。或者如果在超时之前没有处理任何事件,则返回。您可以说它类似于异步 NSURLConnections,在后台处理数据而不会干扰您当前的循环,但同时,您需要同步数据。这可以在 RunLoop 的帮助下完成,它使您的异步NSURLConnection并在调用时提供数据。您可以像这样使用 RunLoop:

NSDate *loopUntil = [NSDate dateWithTimeIntervalSinceNow:0.1];

while (YourBoolFlag && [[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode beforeDate:loopUntil]) {
    loopUntil = [NSDate dateWithTimeIntervalSinceNow:0.1];
}

在此 RunLoop 中,它将一直运行,直到您完成一些其他工作并将YourBoolFlag设置为false

同样,您可以在线程中使用它们。

希望这对您有所帮助。

于 2014-10-14T09:30:54.617 回答
1

运行循环是与线程相关的基础架构的一部分。运行循环是一个事件处理循环,用于安排工作和协调接收传入事件。运行循环的目的是在有工作要做时让你的线程保持忙碌,并在没有工作时让你的线程进入睡眠状态。

从这里


CFRunLoop 最重要的特性是 CFRunLoopModes。CFRunLoop 与“运行循环源”系统一起工作。源在一个或多个模式的运行循环上注册,并且运行循环本身在给定模式下运行。当事件到达源时,只有源模式与运行循环当前模式匹配时才会由运行循环处理。

从这里

于 2018-04-25T01:50:26.953 回答
0
Swift
let runLoop = RunLoop.current

Obj-c
NSRunLoop * runloop = [NSRunLoop currentRunLoop];

运行循环是一个事件处理循环,用于持续监控和处理输入事件,并将其分配给相应的目标进行处理。

于 2021-07-20T01:55:18.467 回答