Swift:创建一个多功能多播委托

时间:2017-01-09 16:42:37

标签: ios swift

我想在事情发生变化时使用多播委托通知多个对象。我读过的教程解释了这个,有一个协议,只有一个函数直接在代理数组上调用。当只定义了一个函数时,它工作正常。我的协议有6个功能。我想避免创建6个单独的函数,并重用一个可以应用于我的代理数组的函数。

简单的例子:(我知道这不起作用,但我只是想了解我的想法。

protocol MyProtocol {
 func method1()
 func method2()
 func method3()
}


class TestClass {
  var delegates = [MyProtocol]()

  func invokeDelegates(delegateMethod: () -> ()) {
    for delegate in delegates {
      delegate.delegateMethod()
    }
  }
}

显而易见的问题是编译器抱怨原始协议中没有定义“delegateMethod”。有没有办法将该方法转换为MyProtocol的一部分,编译器会信任我吗?

这甚至可能吗?

2 个答案:

答案 0 :(得分:18)

以下是我在项目中使用的多播委托模式的要点。它还可以防止强大的参考周期(内存泄漏)。 WeakWrapper处理这个问题。

确定。在一些解决方案中,我看到了错误(强大的保留周期,竞争条件......)

以下是基于1天研究的结果。对于代理堆栈,我使用了NSHashTable,因此所有代理都有弱引用。

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

答案 1 :(得分:7)

您需要更改invokeDelegates的签名以获取类型(MyProtocol) -> ()的闭包,然后您需要将每个委托传递给闭包。

protocol MyProtocol {
    func method1()
    func method2()
    func method3()
}

class TestClass {
    var delegates = [MyProtocol]()

    func invokeDelegates(delegateMethod: (MyProtocol) -> ()) {
        for delegate in delegates {
            delegateMethod(delegate)
        }
    }
}

闭包应该只在其参数上调用适当的委托方法。 Swift可以推断出闭包的参数和返回类型,你可以使用简写$0来引用参数,所以闭包可以很短:

let tester = TestClass()
tester.invokeDelegates(delegateMethod: { $0.method1() })

另一方面,您可以直接在Collection.forEach数组上使用delegates(如果可以访问)并跳过invokeDelegates方法:

tester.delegates.forEach { $0.method1() }