【问题标题】:Using KVC with Singleton pattern将 KVC 与单例模式一起使用
【发布时间】:2017-12-06 01:55:38
【问题描述】:

我的问题是是否可以在 Swift 上的 Singleton 属性上使用 KVC。我在一个类上测试 KVC 能够让它工作,但决定看看它是否在 Singleton 类上工作。
我遇到了一个错误,指出我的 Singleton 的“共享”属性不符合 KVC。

 class KVOObject: NSObject {
    @objc static let shared = KVOObject()
    private override init(){}

    @objc dynamic var fontSize = 18
 }

 override func viewDidLoad() {
    super.viewDidLoad()

    addObserver(self, forKeyPath: #keyPath(KVOObject.shared.fontSize), options: [.old, .new], context: nil) 
 }

 override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
   if keyPath == #keyPath(KVOObject.shared.fontSize) {
      // do something
   }
 }

我目前收到以下错误:

NetworkCollectionTest[9714:452848] *** 由于未捕获的异常 'NSUnknownKeyException' 导致应用程序终止,原因:'[ a​​ddObserver: forKeyPath:@"shared.fontSize" options:3 context:0x0] 被发送到一个对象,该对象是“共享”属性不符合 KVC。'

【问题讨论】:

  • 密钥路径是#keyPath(KVOObject.fontSize)吗?
  • 使用#keyPath(KVOObject.fontSize) 确实有效,但是当属性KVOObject.shared.fontSize 发生更改时,不会观察到
  • 行得通!谢谢

标签: ios swift singleton kvc


【解决方案1】:

密钥路径不正确。这是KVOObject.fontSize。您需要将观察者添加到该单例中:

 KVOObject.shared.addObserver(self, forKeyPath: #keyPath(KVOObject.fontSize), options: [.old, .new], context: nil)

顺便说一句,(a)您可能应该使用上下文来确定您是否正在处理它或者它是否可能被超类使用; (b) 如果不是您的,您应该调用 super 实现; (c) 确保删除 deinit 上的观察者:

class ViewController: UICollectionViewController {

    private var observerContext = 0

    override func viewDidLoad() {
        super.viewDidLoad()

        KVOObject.shared.addObserver(self, forKeyPath: #keyPath(KVOObject.fontSize), options: [.new, .old], context: &observerContext)
    }

    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        if context == &observerContext {
            // do something
        } else {
            super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
        }
    }

    deinit {
        KVOObject.shared.removeObserver(self, forKeyPath: #keyPath(KVOObject.fontSize))
    }

    ...
}

或者,如果在 Swift 4 中,它现在更容易了,因为它是基于闭包的(避免需要上下文)并且当 NSKeyValueObservation 超出范围时会自动删除:

class ViewController: UICollectionViewController {

    private var token: NSKeyValueObservation?

    override func viewDidLoad() {
        super.viewDidLoad()

        token = KVOObject.shared.observe(\.fontSize, options: [.new, .old]) { [weak self] object, change in
            // do something
        }
    }

    ...
}

顺便说一下,对单例的一些观察:

  1. shared 属性不需要@objc 限定符;只有被观察的财产才需要;和

  2. init 方法确实应该调用super;和

  3. 我可能还会将其声明为 final 以避免可能导致子类化单例的混淆。

因此:

final class KVOObject: NSObject {
    static let shared = KVOObject()

    override private init() { super.init() }

    @objc dynamic var fontSize: Int = 18
}

【讨论】:

  • Swift 4 版本在观察单例属性时实际上不起作用。
  • 感谢您的回复。我仔细检查了我的代码,是的,事实证明 Swift 4 的单例 KVO 工作得很好。
  • 我还发现,当你声明静态单例属性时,你甚至不需要@objc 关键字。所以static let shared = KVOObject() 会起作用。对于观察到的属性,您只需要 @objc dynamic
  • 正确,这是不必要的(例如,有关 KVO 和 Swift 的一般性讨论,请参阅 stackoverflow.com/a/25219216/1271826)。他的单身人士也有其他问题,我已将其添加到答案的末尾。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-06
  • 1970-01-01
  • 2015-05-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多