简体   繁体   English

在Swift的类中向全局变量添加属性观察器

[英]Add property observer to global variable inside class in Swift

I have a variable globalVariable declared at global scope that may change at any time. 我在全局范围内声明了一个变量globalVariable ,该变量可能随时更改。

Different ViewControllers in my app need to react differently, when globalVariable changes. globalVariable更改时,我的应用程序中的不同ViewControllers需要做出不同的反应。

Thus it would be desirable to add a property observer in each ViewController that execute the needed code when globalVariable changes. 因此,需要在globalVariable更改时在执行所需代码的每个ViewController中添加属性观察器。

I cannot seem to achieve it with override or extension . 我似乎无法通过overrideextension来实现它 What is the way to go here? 去这里的路是什么?

If your goal is to simply know when your global variable changed, you could have it post a notification upon change: 如果您的目标是仅知道全局变量何时更改,则可以让它在更改后发布通知:

extension NSNotification.Name {
    static let globalVariableChanged = NSNotification.Name(Bundle.main.bundleIdentifier! + ".globalVariable")
}

var globalVariable: Int = 0 {
    didSet {
        NotificationCenter.default.post(name: .globalVariableChanged, object: nil)
    }
}

Then any object can add an observer for that notification: 然后,任何对象都可以为该通知添加观察者:

class ViewController: UIViewController {

    private var observer: NSObjectProtocol!

    override func viewDidLoad() {
        super.viewDidLoad()

        // add observer; make sure any `self` references are `weak` or `unowned`; obviously, if you don't reference `self`, that's not necessary

        observer = NotificationCenter.default.addObserver(forName: .globalVariableChanged, object: nil, queue: .main) { [weak self] notification in
            // do something with globalVariable here
        }
    }

    deinit {
        // remember to remove it when this object is deallocated

        NotificationCenter.default.removeObserver(observer)
    }

}

Note, this didSet mechanism will not detect changes if (a) the global variable is a reference type, ie a class ; 注意,如果(a)全局变量是引用类型,即class ,则didSet机制将不会检测到更改。 and (b) it merely mutates the object that the global variable references rather than replacing it with a new instance. (b)仅更改全局变量引用的对象,而不用新实例替换它。 To identify that scenario, you need to use KVO or other mechanism to detect mutation. 要确定该情况,您需要使用KVO或其他机制来检测突变。

There can be only one didSet{} function for your global variable and it must belong to the variable itself. 全局变量只能有一个didSet {}函数,它必须属于变量本身。 What you can do is make the variable's didSet{} function call a list of functions from other objects. 您可以做的是使变量的didSet {}函数从其他对象调用函数列表。

You could use notifications for this or you could build your own mechanism. 您可以为此使用通知,也可以构建自己的机制。

Here's an example of how you could create your own mechanism: 这是如何创建自己的机制的示例:

(note that this is pretty generic and could work for any variable types or singleton instance) (请注意,这是非常通用的,可以用于任何变量类型或单例实例)

// Container for an observer's function reference
// - will be used to call the observer's code when the variable is set
// - Separates the object reference from the function reference
//   to avoid strong retention cycles.
struct GlobalDidSet<T>
{
   weak var observer:AnyObject?
   var didSetFunction:(AnyObject)->(T)->()
   init(_ observer:AnyObject, didSet function:@escaping (AnyObject)->(T)->())
   {
      self.observer  = observer
      didSetFunction = function
   }
}

// Container for a list of observers to be notified
// - maintains the list of observers
// - automatically clears entries that non longer have a valid object
// - calls all observers when variable changes
// - erases type of observer to allow generic use of GlobalDidSet<>
struct GlobalDidSets<T>
{
   var observers : [GlobalDidSet<T>] = []
   mutating func register<O:AnyObject>(_ observer:O, didSet function:@escaping (O)->(T)->())
   {
      let observer = GlobalDidSet<T>(observer)
      { (object:AnyObject) in function(object as! O) }
      observers.append(observer)  
   }

   mutating func notifyDidSet(_ oldValue:T)
   {
      observers = observers.filter{$0.observer != nil}
      observers.forEach{ $0.didSetFunction($0.observer!)(oldValue) }
   }
}

... ...

// To use this, you will need a second variable to manage the list of observers
// and your global variable's didSet{} must use that observer list
// to perform the multiple function calls
//
var globalVariableDidSets = GlobalDidSets<String>()
var globalVariable : String = "Initial Value"
{
   didSet { globalVariableDidSets.notifyDidSet(oldValue) }
}

// In your view controllers (or any other class), you need to setup the 
// reaction to the global variable changes by registering to the observer list
//
class MyVC:UIViewController
{
    override func viewDidLoad()
    {
      globalVariableDidSets.register(self){ $0.handleVariableChange }  
      // ...
    }

    func handleVariableChange(_ oldValue:String)
    {
      //...
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM