使用KVC和Singleton模式

时间:2017-12-06 01:55:38

标签: ios swift singleton kvc

我的问题是关于是否可以在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'终止应用程序,原因:'[addObserver:forKeyPath:@“shared.fontSize”options:3 context:0x0]被发送到一个对象不符合KVC标准的“共享”财产。'

1 个答案:

答案 0 :(得分:3)

关键路径不正确。它是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以避免混淆,导致子类化单例。

  4. 因此:

    final class KVOObject: NSObject {
        static let shared = KVOObject()
    
        override private init() { super.init() }
    
        @objc dynamic var fontSize: Int = 18
    }