繁体   English   中英

Swift中的观察者模式

[英]Observer Pattern in Swift

我想实现一个观察者模式,但我没有在Swift(也是2.0)中找到合适的编程语言结构。 主要问题是:

  1. protocolextension不允许存储属性。
  2. 在类中,我们可以添加存储的属性,但是我们不能强制子类覆盖它的一些继承的方法。

这就是我要的:

{class|protocol|extension|whathaveyou} Sensor {
    var observers = Array<Any>() // This is not possible in protocol and extensions 
    // The following is does not work in classes
    func switchOn() 
    func switchOff()
    var isRunning : Bool {
        get
    }
}

class LightSensor : Sensor {
    //...
    override func switchOn() {
        // turn the sensor on
    }
}

// In the class C, implementing the protocol 'ObserverProtocol'

var lightSensor = LightSensor()
lightSensor.switchOn()
lightSensor.registerObserver(self) // This is what I want

以下是我的知识:

class Sensor {
    private var observers = Array<Observer>()

    func registerObserver(observer:ObserverDelegate) {
        observers.append(observer)
    }
}

protocol SensorProtocol {
    func switchOn()
    func switchOff()
    var isRunning : Bool {
        get
    }
}

class LightSensor : Sensor, SensorProtocol {
    func switchOn() {
        //
    }
    func switchOff() {
        //
    }

    var isRunning : Bool {
        get {
            return // whatever
        }
    }
}

但这不是很方便,因为SensorSensorProtocol都应该齐头并进,并且都是LightSensor必须实现的子类的要求。

有任何想法吗?

协议是在许多(可能非常不同的)其他对象之间共享的一组抽象需求。 因此,将数据存储在协议中是不合逻辑的。 这就像全球国家。 我可以看到你想要定义观察者如何存储的规范。 这也将允许'你'将'别人'从观察者身上移除,而且对观察者的存储方式也有很大限制。

因此,您的协议应该公开添加和删除“您自己”作为观察者的方法。 然后,实现协议的对象负责决定观察者的存储方式和位置,并实现添加和删除。


您可以创建一个结构来使用您的协议,例如:

protocol Observer: class {
    func notify(target: Any)
}

protocol Observable {
    mutating func addObserver(observer: Observer)
    mutating func removeObserver(observer: Observer)
}

struct Observation: Observable {
    var observers = [Observer]()

    mutating func addObserver(observer: Observer) {
        print("adding")
        observers.append(observer)
    }
    mutating func removeObserver(observer: Observer) {
        print("removing")
        for i in observers.indices {
            if observers[i] === observer {
                observers.removeAtIndex(i)
                break
            }
        }
    }
    func notify(target: Any) {
        print("notifying")
        for observer in observers {
            observer.notify(target)
        }
    }
}

struct ATarget: Observable {
    var observation = Observation()

    mutating func addObserver(observer: Observer) {
        observation.addObserver(observer)
    }
    mutating func removeObserver(observer: Observer) {
        observation.removeObserver(observer)
    }

    func notifyObservers() {
        observation.notify(self)
    }
}

class AnObserver: Observer {
    func notify(target: Any) {
        print("notified!")
    }
}

let myObserver = AnObserver()
var myTarget: Observable = ATarget()
myTarget.addObserver(myObserver)

if let myTarget = myTarget as? ATarget {
    myTarget.notifyObservers()
}

这是我在Swift 3中的解决方案

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        var objectToObserve = ObjectToObserve()

        let observer = Observer()
        let observer1 = Observer()

        objectToObserve.add(observer: observer, notifyOnRegister: true)
        objectToObserve.add(observer: observer1, notifyOnRegister: true)
    }
}

//
// MARK: Protocol
//
protocol Observing: class {
    func doSomething()
    func doSomethingClosure(completion: () -> Void)
}

protocol Observable {

}

extension Observable {

    private var observers: [Observing] {
        get {
            return [Observing]()
        }
        set {
            //Do nothing
        }
    }

    mutating func add(observer: Observing, notifyOnRegister: Bool) {
        if !observers.contains(where: { $0 === observer }) {
            observers.append(observer)

            if notifyOnRegister {
                observer.doSomething()
                observer.doSomethingClosure(completion: {
                    print("Completion")
                })
            }
        }
    }

    mutating func remove(observer: Observing) {
        observers = observers.filter({ $0 !== observer })
    }
}

//
// MARK: Observing
//
class ObjectToObserve: Observable {

    init() {
        print("Init ObjectToObserve")
    }
}

class Observer: Observing {

    init() {
        print("Init Observer")
    }

    func doSomething() {
        print("Do something")
    }

    func doSomethingClosure(completion: () -> Void) {
        print("Do something Closure")
        completion()
    }
}

好吧,你当然可以克服在扩展上没有存储属性的限制。 也许这样,您可以使用扩展来补充提议的解决方案之一,该扩展可帮助您避免在每个子类/协议实现中创建观察者列表。

虽然扩展不能存储属性,但实际上可以使用Objective-C Runtime来获取它们。 假设您有传感器的基类(BaseSensor)和观察者协议(SensorObserver):

import Foundation
import ObjectiveC

private var MyObserverListKey: UInt8 = 0

extension BaseSensor {
  var observers:[SensorObserver] {
    get {
      if let observers = objc_getAssociatedObject( self, &MyObserverListKey ) as? [SensorObserver] {
        return observers
      }
      else {
        var observers = [SensorObserver]()
        objc_setAssociatedObject( self, &MyObserverListKey, observers, objc_AssociationPolicy(OBJC_ASSOCIATION_RETAIN_NONATOMIC) )
        return observers
      }
    }
    set(value) {
      objc_setAssociatedObject( self, &MyObserverListKey, observers, objc_AssociationPolicy(OBJC_ASSOCIATION_RETAIN_NONATOMIC) )
    }
  }
}

要明确的是,即使您需要BaseSensor和所有传感器继承它以获得此属性,BaseSensor也不会实际实现传感器协议。 这有点奇怪,但我认为它会满足您的需求:

class BaseSensor {
}

protocol Sensor {
   func switchOn()
}

class LightSensor: BaseSensor, Sensor {
   func switchOn() {
     // whatever
   }
}

使用Swift 2.0,这会更简单,因为您可以使用Protocol Extensions,因此您可以简单地执行此操作:

protocol Sensor {
  func switchOn()
}

extension Sensor {
  // Here the code from the previous implementation of the extension of BaseSensor
}

class LightSensor : Sensor {
   func switchOn() {
     // whatever
   }
}

方式更好。

上面的所有答案都错误地使用数组来保留观察者,由于强引用,这可能会创建保留周期。

通常,您可能不希望允许同一个观察者自己注册两次。

所提出的解决方案也不是通用的或缺乏类型安全性。 我在这里引用我的博客文章,以一种Swifty方式呈现完整的解决方案:

https://www.behindmedia.com/2017/12/23/implementing-the-observer-pattern-in-swift/

暂无
暂无

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

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