8

我正在尝试在 Swift 中实现多播委托功能。在 Objective C 中,我们有这个出色的实现

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

我刚刚创建了这个基本功能:

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

}

我的问题是我似乎无法找到一种方法来做到这一点:

delegate.testString()

为了向节点中的所有代表提供命令“testString()”。谁能帮我这个?

4

6 回答 6

9

斯威夫特 3实现:

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

您可以使用以下方法对其进行测试:

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

它打印:

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.

基于这篇博文

于 2016-11-10T23:33:33.203 回答
3

一个关于 MulticastDelegate 的简单演示。

class DelegateMulticast <T> {

  private var delegates = [T]()

  func addDelegate(delegate: T) {
    delegates.append(delegate)
  }

  func invokeDelegates(invocation: (T) -> ()) {
    for delegate in delegates {
        invocation(delegate)
    }
  }
}

protocol MyProtocol {
   func testString() -> String
}


class OP {
var delegates = DelegateMulticast<MyProtocol>()

  func process(){
    delegates.invokeDelegates{
        $0.testString()
    }
  }
}
于 2015-05-10T13:02:09.680 回答
3

好的。在某些解决方案中,我看到了错误(强保留周期、竞争条件……)

这是我根据 1 天的研究结合起来的。对于委托堆栈,我使用了 NSHashTable,因此所有委托都具有弱引用。

斯威夫特 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)
}



如何设置委托:

object.delegates.add(delegate: self)



如何在代表上执行功能: 而不是

delegate?.delegateFunction

你用

delegates.invoke(invocation: { $0.delegateFunction })
于 2017-06-22T15:53:31.990 回答
2

这是我使用 Swift 2.0 协议扩展实现的多播委托。我还增加了删除代表的能力。为此,我使我的委托类型符合 NSObjectProtocol,但不知道如何声明它应该是使用 === 运算符进行删除的引用类型。

protocol MulticastDelegateContainer {

    typealias DelegateType : NSObjectProtocol
    var multicastDelegate  : [DelegateType] {set get}
}

extension MulticastDelegateContainer {

    mutating func addDelegate(delegate : DelegateType) {
        multicastDelegate.append(delegate)
    }

    mutating func removeDelegate(delegate : DelegateType) {
        guard let indexToRemove = self.multicastDelegate.indexOf({(item : DelegateType) -> Bool in
            return item === delegate
        }) else {return}

        multicastDelegate.removeAtIndex(indexToRemove)
    }

    func invokeDelegate(invocation: (DelegateType) -> ()) {
        for delegate in multicastDelegate {
            invocation(delegate)
        }
    }
}

这是使用示例

@objc protocol MyProtocol : NSObjectProtocol {
    func method()
}


class MyClass : MulticastDelegateContainer {
    typealias DelegateType = MyProtocol
    var multicastDelegate = [MyProtocol]()

    func testDelegates() {
        invokeDelegate { $0.method() }
    }
}
于 2015-10-23T11:13:38.540 回答
1

我在 GitHub 上添加了一个 Swift 多播委托的实现:https ://github.com/tumtumtum/SwiftMulticastDelegate

基本上,您使用带有块的重载运算符“=>”来执行调用。MulticastDelegate 在内部将为每个侦听器调用该块。

class Button
{
  var delegate: MulticastDelegate<ButtonDelegate>?

  func onClick()
  {
    self.delegate => { $0.clicked(self) }
  }  
}
于 2016-05-25T10:07:49.357 回答
0

您也许可以添加

@objc

当然,对于您的协议和类,您不再是纯粹的 swift ......但这可能会解决您的问题,因为它将重新启用动态调度能力。

于 2015-03-26T12:09:29.137 回答