一群在斯威夫特的弱观察者

时间:2016-01-16 12:55:35

标签: swift generics observer-pattern weak-references

我正在尝试实现一个允许我存储一组弱观察者的结构。

这是观察者包装器:

<activity android:windowSoftInputMode="adjustPan" >

这是每个观察者需要遵守的协议:

public func ==<T: Hashable>(lhs: WeakObserver<T>, rhs: WeakObserver<T>) -> Bool {
  return lhs.hashValue == rhs.hashValue
}

public struct WeakObserver<T where T: AnyObject, T: Hashable> : Hashable {

  private weak var weakObserver : T?

  public init(weakObserver: T){
    self.weakObserver = weakObserver
  }

  public var hashValue : Int {
    return self.weakObserver!.hashValue
  }

}

用法:

public protocol DataModelObserverProtocol : class, Hashable, AnyObject {

  func someFunc()

}

现在,虽然我知道这可能是Swift本身的限制,但我正在寻找一种替代解决方案,而没有具体的类作为类型约束(如果这不可能,我担心是这种情况,我' d仍然喜欢获得替代的“非黑客”解决方案。)

3 个答案:

答案 0 :(得分:2)

使用Set来保存引用可能会导致Set最终需要使用其hashValue引用元素,并且当弱引用为nil时,hashValue函数将崩溃。

我无法通过协议来实现它,但我找到了一种使用返回函数元组的泛型函数来获得类似功能的方法。

struct WeakReference<T>
{
    weak var _reference:AnyObject?
    init(_ object:T) {_reference = object as? AnyObject}
    var reference:T? { return _reference as? T }
}

func weakReferences<T>(_:T.Type) -> ( 
                                     getObjects: ()->[T],
                                     addObject:  (T)->()
                                    )
{  
   var references : [WeakReference<T>] = []

   func getObjects() -> [T]
   { 
      return references.filter({ $0.reference != nil }).map({$0.reference!}) 
   }

   func addObject(object:T)
   { 
      if getObjects().contains({ ($0 as! AnyObject) === (object as! AnyObject) }) 
      { return }
      references.append(WeakReference(object)) 
   } 

  return (getObjects:getObjects, addObject:addObject)   
}

public protocol DataModelObserverProtocol: class, AnyObject
{
   func someFunc() -> String
}

public class DataModel: NSObject, DataModelInterface  
{
   var observers = weakReferences(DataModelObserverProtocol)
}

要添加观察者,您可以使用:

observers.addObject( yourObserver )

迭代观察者:

for observer in observers.objects()
{
   observer.someFunc()
}

这两个函数都是类型安全的,只接受/返回符合DataModelObserverProtocol的对象

答案 1 :(得分:1)

我遇到了同样的问题,并提出了这个出奇的简单解决方案。从一个无骨的薄弱容器开始:

class WeakObserverBox {
    weak var unbox: Observer?

    init(_ observer: Observer) {
        self.unbox = observer
    }
}

将您的存储声明为字典,并使用唯一的ObjectIdentifier作为键。随后是一个方便的计算属性(该属性将仅包含在调用时还处于活动状态的观察者):

var observerDict: [ObjectIdentifier: WeakObserverBox] = [:]
var observers: [Observer] {
    return observerDict.values.compactMap { $0.unbox }
}

所有观察者的操作都通过observers数组完成。添加和删​​除如下:

func add(_ observer: O) {
    observerDict[ObjectIdentifier(observer)] = WeakObserverBox(observer)
}

func remove(_ observer: Observer) {
    observerDict.removeValue(forKey: ObjectIdentifier(observer))
}

顺便说一句,如果您需要修剪过期的引用,可以在此getter或add方法(或根据您的性能考虑,两者都可以)中完成此操作,

for observerKey in observerDict.keys {
    if observerDict[observerKey]?.unbox == nil {
        observerDict.removeValue(forKey: observerKey)
    }
}

答案 2 :(得分:0)

我知道有更好的方法可以做到这一点,但对于这样一个简单的概念来说似乎太复杂了,所以我采取了不同的方式:

我刚刚使用NSNotificationCenter并重构我的代码而不必具有紧密耦合的结构,因此我设法以一种对我有用的方式抽象它而不是从通知中传递信息。通知不需要将参数传递给他们的观察者。由于通知仅支持通过userInfo字典传递信息,因此我不得不考虑这一点。

因此,正如我所提到的,我需要一种上下文解耦的方式。 这是一个非常抽象的答案,但我认为它可能有助于某人避免不得不捣乱#34;功能失调的泛型&#34;。

要通知我的观察员,我只需注册:

NSNotificationCenter.defaultCenter().addObserver(...)

并发布不带参数的通知:

NSNotificationCenter.defaultCenter().postNotification...

我之前的项目中已经使用了内置的NSNotificationCenter并且我完全了解它的内部工作原理,但最初我打算将信息传递给观察者,这就是我想限制它的原因更多的创造WeakSet种类。

这也适用于我的情况,因为我的观察者并不一定需要通过可观察的细节,因为它通常存在并且无论如何都可以被观察者访问(这是我的结构所提供的,它可能不是这种方式在读者的情况下)。很难解释它,所以对任何阅读此内容的人都有意义,但我对提出的WeakSet非常气馁,所以我接受了这一点。它更具可读性(虽然可能对第三方不太了解),但它似乎更适合我的用例。