Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift Language Multicast Delegate

I am trying to implement the multicast delegate functionality in Swift. In Objective C, we have this excellent implementation

https://github.com/robbiehanson/XMPPFramework/blob/master/Utilities/GCDMulticastDelegate.m

And I have just created this basic functionality:

protocol MyProtocol : class{
    func testString()-> String;
}

class MulticastDelegateNode <T:AnyObject> {
    weak var delegate : T?

    init(object : T){
        self.delegate = object;
    }
}

class MulticastDelegate <T:AnyObject> {
    var delegates = Array<MulticastDelegateNode<T>>()


    func addDelegate(delegate : T){
        var newNode = MulticastDelegateNode(object : delegate);
        delegates.append(newNode);
    }

    func removeDelegate(delegate : AnyObject){
        self.delegates = self.delegates.filter({ (node : MulticastDelegateNode) -> Bool in
            return node.delegate !== delegate;
        });
    }
}

class OP {
    var delegate  = MulticastDelegate<MyProtocol>();

    func process(){
        //...
        //make actions

        //notify the objects!
    }

}

My problem is that it seems I cannot figure out a way to do this:

delegate.testString()

In order to give the command 'testString()' to all delegates that are in the nodes. Can anyone help me with this?

like image 862
csotiriou Avatar asked Nov 16 '14 15:11

csotiriou


People also ask

What is multicast delegate Swift?

MulticastDelegate is a utility class that keeps track of delegates in an array and invokes arbitrary blocks of code on them. And to avoid retain cycles, we chose NSHashTable to hold weak references to delegates.

What is multicast delegate?

The multicast delegate contains a list of the assigned delegates. When the multicast delegate is called, it invokes the delegates in the list, in order. Only delegates of the same type can be combined. The - operator can be used to remove a component delegate from a multicast delegate.

How do I set delegates in Swift?

Key Steps to DelegationCreate a delegate protocol that defines the messages sent to the delegate. Create a delegate property in the delegating class to keep track of the delegate. Adopt and implement the delegate protocol in the delegate class. Call the delegate from the delegating object.

What is delegate protocol in Swift?

Protocol: A set of methods that would be implemented by the class which conforms to that protocol. Delegate: The reference to that class which conforms to the protocol and will adhere to implement methods defined in the protocol.


2 Answers

Swift 3 implementation:

class MulticastDelegate<T> {
    private var delegates = [Weak]()

    func add(_ delegate: T) {
        if Mirror(reflecting: delegate).subjectType is AnyClass {
            delegates.append(Weak(value: delegate as AnyObject))
        } else {
            fatalError("MulticastDelegate does not support value types")
        }
    }

    func remove(_ delegate: T) {
        if type(of: delegate).self is AnyClass {
            delegates.remove(Weak(value: delegate as AnyObject))
        }
    }

    func invoke(_ invocation: (T) -> ()) {
        for (index, delegate) in delegates.enumerated() {
            if let delegate = delegate.value {
                invocation(delegate as! T)
            } else {
                delegates.remove(at: index)
            }
        }
    }
}

private class Weak: Equatable {
    weak var value: AnyObject?

    init(value: AnyObject) {
        self.value = value
    }
}

private func ==(lhs: Weak, rhs: Weak) -> Bool {
    return lhs.value === rhs.value
}

extension RangeReplaceableCollection where Iterator.Element : Equatable {
    @discardableResult
    mutating func remove(_ element : Iterator.Element) -> Iterator.Element? {
        if let index = self.index(of: element) {
            return self.remove(at: index)
        }
        return nil
    }
}

You can test it with:

protocol SomeDelegate: class {
    func onSomeEvent()
}

class SomeDelegateImpl: SomeDelegate {
    let value: Int

    init(value: Int) {
        self.value = value
    }

    func onSomeEvent() {
        print("Invoking delegate \(value)")
    }
}

let multicastDelegate = MulticastDelegate<SomeDelegate>()

func testInvoke() {
    multicastDelegate.invoke {
        $0.onSomeEvent()
    }
}

print("Adding first delegate.")

let delegate1 = SomeDelegateImpl(value: 1)

multicastDelegate.add(delegate1)

testInvoke()

let delegate2 = SomeDelegateImpl(value: 2)

print("Adding second delegate.")

multicastDelegate.add(delegate2)

testInvoke()

print("Removing first delegate.")
multicastDelegate.remove(delegate1)

testInvoke()

print("Adding third delegate.")

({
    let delegate3 = SomeDelegateImpl(value: 3)
    multicastDelegate.add(delegate3)
    testInvoke()
})()

print("Third delegate is deallocated by ARC.")

testInvoke()

It prints:

Adding first delegate.
Invoking delegate 1.
Adding second delegate.
Invoking delegate 1.
Invoking delegate 2.
Removing first delegate.
Invoking delegate 2.
Adding third delegate.
Invoking delegate 2.
Invoking delegate 3.
Third delegate is deallocated by ARC.
Invoking delegate 2.

Based on this blog post.

like image 139
mixel Avatar answered Oct 03 '22 12:10

mixel


Ok. In some of the solutions I see mistakes (strong retain cycles, race conditions, ...)

Here is what I combine based on 1 day research. For the stack of delegates I used NSHashTable, so all the delegates are having weak reference.

Swift 3.1

class MulticastDelegate <T> {
  private let delegates: NSHashTable<AnyObject> = NSHashTable.weakObjects()

  func add(delegate: T) {
    delegates.add(delegate as AnyObject)
  }

  func remove(delegate: T) {
    for oneDelegate in delegates.allObjects.reversed() {
      if oneDelegate === delegate as AnyObject {
        delegates.remove(oneDelegate)
      }
    }
  }

  func invoke(invocation: (T) -> ()) {
    for delegate in delegates.allObjects.reversed() {
      invocation(delegate as! T)
    }
  }
}

func += <T: AnyObject> (left: MulticastDelegate<T>, right: T) {
  left.add(delegate: right)
}

func -= <T: AnyObject> (left: MulticastDelegate<T>, right: T) {
  left.remove(delegate: right)
}



How to set delegate:

object.delegates.add(delegate: self)



How to execute function on the delegates: instead of

delegate?.delegateFunction

you use

delegates.invoke(invocation: { $0.delegateFunction })
like image 35
Klemen Avatar answered Oct 03 '22 14:10

Klemen