委托

一个旨在提供一个更优秀的 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 之前,还需要检查 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,并在委托被调用时,向你提供 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,这样可以避免这类问题。

待办事项