1

的 A类具有B类的属性,可以重置:

class A1 {
    private var b = B(0)

    func changeB(i : Int) {
        b = B(i)
    }

    func testB(k : Int) -> Bool {
        return b.test(k)
    }
}

class B {
    private let b : Int;

    init(_ i : Int) {
        b = i
    }

    func test(_ k : Int) -> Bool {
        return b == k
    }
}

到目前为止,一切都很好。如果我想在多线程场景中使用A类,我必须添加一些同步机制,因为 Swift中的属性本身不是原子的:

class A2 {
    private var b = B(0)
    private let lock = NSLock()

    func changeB(i : Int) {
        lock.lock()
        defer { lock.unlock() }
        b = B(i)
    }
    
    func testB(k : Int) -> Bool {
        lock.lock()
        defer { lock.unlock() }
        return b.test(k)
    }

}

但现在我想介绍一个闭包:

class A3 {
    func listenToB() {
        NotificationCenter.default.addObserver(forName: Notification.Name("B"), object: nil, queue: nil) { 
        [b] (notification) in
            let k = notification.userInfo!["k"] as! Int
            print(b.test(k))
        }
    }
}

我是否正确理解这不是线程安全的?如果我也捕获,这会得到解决lock吗,如下所示?

class A4 {
    func listenToB() {
        NotificationCenter.default.addObserver(forName: Notification.Name("B"), object: nil, queue: nil) { 
        [lock, b] (notification) in
            let k = notification.userInfo!["k"] as! Int
            lock.lock()
            defer { lock.unlock() }
            print(b.test(k))
        }
    }
}
4

1 回答 1

1

是的,使用捕获lock可确保观察者的闭包与使用相同锁的其他任务同步。您可以使用这种捕获模式,因为它lock恰好是一个常数。

这引发了更根本的问题,即b参考的捕获,它不是恒定的。这意味着如果您changeB在某个中间时间点调用,您的通知块仍将引用原始捕获B的,而不是新的。

weak self所以,如果你想让它引用当前的,你真的想回退到模式B

class A {
    func listenToB() {
        NotificationCenter.default.addObserver(forName: Notification.Name("B"), object: nil, queue: nil) { [weak self] notification in
            guard let self = self else { return }

            let k = notification.userInfo!["k"] as! Int
            self.lock.lock()
            defer { self.lock.unlock() }
            print(self.b.test(k))
        }
    }
}
于 2021-02-16T16:15:29.283 回答