RunLoopObserver 和 RunLoopActivity

RunLoopObserver and RunLoopActivity

在下面显示的代码中,我创建了一个线程,该线程创建 0 到 15 之间的随机数,当它出现 3 时停止,更改结束参数。在我向主线程的 运行 循环添加了一个 运行 循环观察器(即 "observe" 结束参数)之后。如您所见,运行 循环观察者和我的线程都在打印前休眠 1 秒,因此我希望在控制台中,观察者的打印和我的线程的打印是交替的。事实并非如此。我相信,如果我理解它,它将取决于 CFrunloopActivity 参数及其可能的组合。

有谁能解释一下这个参数的操作吗? 如果是,是否有交替打印的组合?如果不能交替打印,观察者如何在主线程的 运行 循环中工作?

谢谢

这是代码:

class ViewController: UIViewController {
    var end = false

    override func viewDidLoad() {
        super.viewDidLoad()

        //my thread
        performSelector(inBackground: #selector(rununtil3(thread:)), with: Thread.current)

        //the observer
        let runLoopObserver = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, CFRunLoopActivity.entry.rawValue | CFRunLoopActivity.exit.rawValue , true , 0 , {

        (observer: CFRunLoopObserver?, activity: CFRunLoopActivity) -> Void in

        Thread.sleep(until: Date(timeIntervalSinceNow: 1))
        print("+++ is main?: \(Thread.isMainThread)")
        if self.end == true {
            //print the end of my thread and remove the observer from main run loop
            print("end of own thread")
            CFRunLoopRemoveObserver(CFRunLoopGetCurrent(), observer, CFRunLoopMode.commonModes)
            return
        }

        //CFRunLoopRemoveObserver(CFRunLoopGetCurrent(), observer, CFRunLoopMode.commonModes)

        })
        //add observer to main run loop
        CFRunLoopAddObserver(CFRunLoopGetCurrent(), runLoopObserver, CFRunLoopMode.commonModes)

        print("Out of observer")

     }

    func rununtil3(thread : Thread) {
    print("main?: \(thread.isMainThread) is run : \(thread.isExecuting)")
        while true {
            let ran = Int (arc4random() % 15 )
            Thread.sleep(until: Date(timeIntervalSinceNow: 1))
            print("\(ran) . main is run : \(thread.isExecuting)")
            if ran == 3 {
                end = true
                Thread.exit()
            }
        }
    }
}

您正在创建一个 runloop 观察器并要求在 runloop 开始运行(其 enter 事件)或 exits 时收到通知。记录了 Runloop 活动 here

enter 当 runloop 被 CFRunLoopRun() 或类似的启动时,事件被传送。如果您手动创建一个 runloop,向其添加一个 enter 观察者,然后在您的新 runloop 上调用 CFRunLoopRun(),您将在那时收到一个 enter 事件。如果您稍后在运行循环中调用 CFRunLoopStop(),您将收到一个 exit 事件。

当您将 enter 观察者添加到 已经 运行 runloop 时,您将收到一个 enter 事件。这是为了让你的观察者状态与runloop的实际状态保持一致。

在您的代码中,您创建了一个运行循环观察器,然后将其附加到主线程的运行循环(又名 "Main Runloop")。

OS 在程序启动时自动为您创建 Main Runloop 并自动调用 CFRunLoopRun()CFRunLoopStop() 永远不会被调用,因此主运行循环有效地永远运行。

由于主运行循环已经 运行,您收到一个 enter 事件。由于主运行循环不会停止,因此您永远不会看到 exit 事件。

重要的是要注意,runloop observer 绑定到您添加它的特定 runloop,而不是某个任意后台线程的生命周期或 属性(即您的 end 属性 是 不是 正在观察的东西)。


关于如何让线程交替的第二个问题,这是一个答案非常广泛的问题,它在很大程度上取决于您想要做什么。我不会在这里尝试回答所有这些问题,只会给你一些想法。

  1. 您最好根本不创建后台线程,而是向每秒触发的主运行循环添加一个计时器。然后你会得到周期性的行为。

  2. 如果你真的想使用后台线程,那么你应该阅读一本关于线程通信和同步的操作系统书籍。 iOS/OS X 上常见的做法是使用后台线程,然后使用 DispatchQueue.main.async { } 之类的东西向主线程发信号通知您的处理已完成。如果你搜索一下,有很多关于如何做到这一点的例子。

  3. 您还可以阅读有关使用信号量或条件变量进行线程同步的内容。您绝对NOT 想做的一件事是在主线程上调用 Thread.sleep(),就像您在观察者回调中所做的那样。如果您在主线程上等待的时间过长,操作系统将杀死您的应用程序。最好让后台线程完全独​​立于主线程,然后使用我在上面 #2 中提到的 DispatchQueue 调用回调到主线程。