Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Set of weak observers in Swift

I am trying to implement a structure that allows me to store a set of weak observers.

Here is the observer wrapper:

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
  }

}

And here is the protocol every observer needs to conform to:

public protocol DataModelObserverProtocol : class, Hashable, AnyObject {

  func someFunc()

}

Usage:

public class DataModel: NSObject, DataModelInterface {

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

Now, while I am aware that this might be a limitation by Swift itself, I am looking for an alternative solution without having a concrete class as type constraint (if that's not possible, which I am afraid is the case, I'd still love to get alternative "non-hacky" solutions).

like image 597
the_critic Avatar asked Jan 16 '16 12:01

the_critic


1 Answers

Using a Set to hold the references runs a risk that the Set will eventually need to reference an element using its hashValue and, when the weak reference goes nil, the hashValue function will crash.

I couldn't do it with protocols but I found a way to get a similar functionality using a generic function that returns a tuple of functions.

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

To add observers, you would use:

observers.addObject( yourObserver )

To iterate through the observers :

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

Both functions are type safe and will only accept/return DataModelObserverProtocol compliant objects

like image 57
Alain T. Avatar answered Oct 22 '22 13:10

Alain T.