如何确保 CLLocationManager 在主线程上延迟实例化?

How can I make sure that a CLLocationManager is lazily instantiated on the main thread?

class Foo {
static let sharedInstance = Foo() // singleton
private override init() {}

lazy var locationManager: CLLocationManager = {
    let manager = CLLocationManager()
    return manager
}()

创建了 class Foo 的单例实例,它具有延迟实例化的 CLLocationManager。 Foo 单例实例在后台线程上实例化,但必须在主线程上创建 CLLocationManager。实现该目标的最优雅方式是什么?

您可以将管理器的创建包装到在主 OperationQueue 上运行的操作中,然后等待该操作在您的初始化块中完成:

class Foo {
    static let sharedInstance = Foo()
    private init() {}

    lazy var locationManager: CLLocationManager = {
        var manager: CLLocationManager!
        let op = BlockOperation {
            print("Main thread: \(Thread.isMainThread ? "YES" : "NO")")
            manager = CLLocationManager()
        }
        OperationQueue.main.addOperation(op)
        op.waitUntilFinished()
        return manager
    }()
}

通过将此操作放在主队列上,可以确保设置管理器发生在主线程上(如 print 语句所示)。通过等待操作完成,您可以确保用于初始化惰性 属性 的管理器是 non-nil.

使用这种方法时要小心——如果您最终在主线程之外初始化位置管理器,那么如果主线程也在等待该后台工作完成,则可能会出现死锁。例如考虑:

 let queue = OperationQueue()
 queue.addOperation {
     let _ = Foo.sharedInstance.locationManager
 }
 queue.waitUntilAllOperationsAreFinished()

这会尝试在后台队列上设置 locationManager,但会阻塞主线程,直到后台工作完成。同时,后台队列试图将工作反弹到主队列以创建 CLLocationManager。由于两个队列都在等待对方,程序将逐渐停止。你需要小心避免这些情况。