如何确保在主线程上懒惰地实例化CLLocationManager?

时间:2018-03-24 20:59:33

标签: swift grand-central-dispatch cllocationmanager lazy-initialization

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

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

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

1 个答案:

答案 0 :(得分:1)

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

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语句证明)。通过等待操作完成,您可以确保用于初始化惰性属性的管理器是非零的。

小心这种方法 - 如果你最终关闭主线程初始化位置管理器,如果主线程也在等待后台工作完成,它可能会死锁。例如,考虑一下:

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

这会尝试在后台队列上设置locationManager,但会阻止主线程直到后台工作完成。同时,后台队列正在尝试将工作弹回到主队列以创建CLLocationManager。由于两个队列都在相互等待,程序将停止运行。你需要小心避免这种情况。