Delegate 1.3.0

Delegate 1.3.0

Wei Wang 维护。



Delegate 1.3.0

  • 作者
  • Wei Wang

Delegate

一个元库,提供更优的这里这里描述的Delegate模式。

用法

而不是使用普通的Apple的协议-代理模式,可以使用一个简单的Delegate对象来通信

class ClassA {
    let onDone = Delegate<(), Void>()

    func doSomething() {
        // ...
        onDone()
    }
}

class MyClass {
    func askClassAToDoSomething() {
        let a = ClassA()
        a.onDone.delegate(on: self) { (self, _) in
            self.jobDone()
        }
        a.doSomething()
    }

    private func jobDone() {
        print("🎉")
    }
}

为什么

与常规委托比较

Delegate使用了更少的代码和紧凑的结构来做到同样的工作。只需比较上面使用正式的协议-代理模式相同的工作。

protocol ClassADelegate {
    func doSomethingIsDone()
}

class ClassA {
    weak var delegate: ClassADelegate?

    func doSomething() {
        // ...
        delegate?.doSomethingIsDone()
    }
}

class MyClass {
    func askClassAToDoSomething() {
        let a = ClassA()
        a.delegate = self
        a.doSomething()
    }

    private func jobDone() {
        print("🎉")
    }
}

extension MyClass: ClassADelegate {
    func doSomethingIsDone() {
        self.jobDone()
    }
}

没有人喜欢编写样板代码,对吗?

onXXX属性比较

初看起来,您可能认为Delegate是一个过剩的工作,并可以用类似以下的存储属性替换

class ClassA {
    var onDoneProperty: (() -> Void)?
    //...
}

这会创建一个强引用,我发现很容易创建一个意外的循环

class MyClass {
    var a: ClassA = ClassA()

    func askClassAToDoSomething() {
        a.onDoneProperty = {
            // Retain cycle!!
            self.jobDone()
        }
    }

您必须记住在大多数情况下使用[weak self]来断开循环,这也要求您在使用它之前检查self

class MyClass {
    var a: ClassA = ClassA()

    func askClassAToDoSomething() {
        a.onDoneProperty = { [weak self] in
            guard let self = self else { return }
            self.jobDone()
        }
    }

又是样板代码!而且,如果onDoneProperty需要在不同层之间持有调用者,事情会变得更加复杂。

如何

Delegate以弱引用的方式内部持有target,并在调用代理时向您提供一个“强引用化”的影子版本的目标。因此,您可以免费获得正确的内存管理,并且完全不需要样板代码,专注于工作。

a.onDone.delegate(on: self) { // This `self` is the delegation target. `onDone` holds a weak ref of it.
    (self, _) in                 // This `self` is a shadowed, non-option type.
    self.jobDone()            // Using of this `self` does not create retain cycle.
}

要传递一些参数或接收返回类型,只需声明Delegate的泛型类型

class DataController {
    let onShouldShowAtIndexPath = Delegate<IndexPath, Bool>()

    func foo() {
        let currentIndexPath: IndexPath = // ...
        let shouldShow: Bool = onShouldShowAtIndexPath(currentIndexPath)
        if shouldShow {
            show()
        }
    }
}

// Caller Side
dataSource.onShouldShowAtIndexPath.delegate(on: self /* : Target */ ) { (self, indexPath) in
    // This block has a type of `(Target, IndexPath) -> Bool`.
    return indexPath.row != 0
}

注意

唯一的注意事项是,请在委托块中始终使用影子版的self。比如,这会导致旧onXXX属性方式的回归并导致保留循环

a.onDone.delegate(on: self) { (_, _) in
    self.jobDone()
}

看起来您可以使用“相同”的self,但实际上在上述代码中您正在使用“真实”的强self。不要将块的首个输入参数标记为_,并始终将其命名为self,这样您就可以防止这种情况。

待办事项

  • 异步支持。