【问题标题】:Set of weak observers in SwiftSwift 中的一组弱观察者
【发布时间】:2016-01-16 12:55:34
【问题描述】:

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

这是观察者包装器:

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()

}

用法:

public class DataModel: NSObject, DataModelInterface {

  public var observers = Set<WeakObserver<DataModelObserverProtocol>>()
  //^^^^^ Using 'DataModelObserverProtocol' as a concrete type conforming to protocol 'AnyObject' is not supported
}

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

【问题讨论】:

  • @Kirsteins 这看起来真的很像我要找的东西,但对我来说也有点矫枉过正(没有明显的原因)
  • @Kirsteins 即使使用了WeakSet,我也会收到错误Using protocol as a concrete type conforming to protocol is not supported
  • 所以真的在寻找一种替代方法,因为协议不支持作为通用约束。

标签: swift generics observer-pattern weak-references


【解决方案1】:

使用 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 的对象

【讨论】:

  • 这种类型转换在编译时处理,只是告诉编译器我们知道变量是兼容类型的。
  • 唯一的开销是几个额外的内存“单词”用于存储和函数调用中的一个间接。它不太可能对性能产生任何有意义的影响。
  • 我认为每个as? 语句都编译为if 指令。
【解决方案2】:

我遇到了同样的问题,并想出了这个非常简单的解决方案。从一个简单的弱容器开始:

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)
    }
}

【讨论】:

  • 相当干净和安全的方法,我喜欢它。
【解决方案3】:

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

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

因此,正如我所提到的,我需要一种上下文解耦的方式来做到这一点。 这是一个非常抽象的答案,但我认为它可能会帮助某些人规避不得不处理“功能失调的泛型”的问题。

为了通知我的观察者,我只需注册他们:

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

并发布不带参数的通知:

NSNotificationCenter.defaultCenter().postNotification...

我在之前的项目中已经使用了内置的NSNotificationCenter,我完全了解它的内部工作原理,但最初我打算将信息传递给观察者,这就是为什么我想对其进行更多限制创建各种WeakSet

这在我的情况下再次起作用,因为我的观察者不一定需要通过 observable 的细节,因为它通常存在并且观察者可以访问(这是我的结构提供的东西,它可能不是那样的读者案例)。很难解释它,以便对任何阅读本文的人都有意义,但我对提议的WeakSet 感到非常沮丧,所以我同意了。它更具可读性(尽管第三方可能不太容易理解),而且它似乎更适合我的用例。

【讨论】:

    猜你喜欢
    • 2015-09-07
    • 1970-01-01
    • 1970-01-01
    • 2017-03-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多