如何确保CLLocationManager在主线程上被惰性实例化?

时间:2021-12-18 20:46:54
class Foo {
static let sharedInstance = Foo() // singleton
private override init() {}

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

A singleton instance of class Foo is created which has a lazily instantiated CLLocationManager. The Foo singleton instance is instantiated on a background thread, but a CLLocationManager must be created on the main thread. What is the most elegant way of achieving that?

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

1 个解决方案

#1


1  

You can wrap up the creation of the manager into an Operation that runs on the main OperationQueue, then wait for that operation to finish inside your initialization block:

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

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
    }()
}

By placing this operation on the main queue, you ensure that setting up the manager happens on the main thread (as evidenced by the print statement). By waiting for the operation to finish, you ensure that the manager used to initialize the lazy property is non-nil.

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

Be careful with this approach — if you do wind up initializing the location manager off the main thread, it's possible to deadlock if the main thread is also waiting on that background work to complete. Consider, for example:

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

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

This tries to set up the locationManager on a background queue, but blocks the main thread until that background work is done. At the same time, the background queue is attempting to bounce work to the main queue to create the CLLocationManager. Since both queues are waiting on each other, the program will grind to a halt. You'll need to be careful to avoid these kinds of situations.

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

#1


1  

You can wrap up the creation of the manager into an Operation that runs on the main OperationQueue, then wait for that operation to finish inside your initialization block:

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

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
    }()
}

By placing this operation on the main queue, you ensure that setting up the manager happens on the main thread (as evidenced by the print statement). By waiting for the operation to finish, you ensure that the manager used to initialize the lazy property is non-nil.

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

Be careful with this approach — if you do wind up initializing the location manager off the main thread, it's possible to deadlock if the main thread is also waiting on that background work to complete. Consider, for example:

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

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

This tries to set up the locationManager on a background queue, but blocks the main thread until that background work is done. At the same time, the background queue is attempting to bounce work to the main queue to create the CLLocationManager. Since both queues are waiting on each other, the program will grind to a halt. You'll need to be careful to avoid these kinds of situations.

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