委托 (Delegated)

Delegated 是一个非常小的包,可以帮助您在使用基于闭包的委托时避免循环引用。

新的 Medium 文章 这里

原始 Medium 文章 (Delegated 0.1.2) 这里

🚨 警告! Delegated 2.0Delegated 0.1.2 不兼容。 如果您不想迁移当前的代码库,请停留在 Delegated 0.1.2。 请参阅 Delegated 0.1.2 的文档 这里。 如果您需要从 0.1.x 迁移到 2.0.x 的任何帮助,请提出一个 issue。

用法

之前

final class TextField {
    var didUpdate: (String) -> () = { _ in }
}

// later...

self.textField.didUpdate = { [weak self] text in
    guard let strongSelf = self else {
        return
    }
    strongSelf.label.text = text
}

之后

final class TextField {
    @Delegated var didUpdate: (String) -> ()
}

// later...

textField.$didUpdate.delegate(to: self) { (self, text) in
    // `self` is weak automatically!
    self.label.text = text
}

没有循环引用! 没有内存泄漏! 没有 [weak self]!🎉

指南

创建一个委托函数

final class TextField {
    @Delegated var didUpdate: (String) -> ()
}

这只会为具有恰好一个参数且没有返回值的闭包编译。 要使用任何其他数量的参数,请使用这个

final class TextField {
    @Delegated0 var didStartEditing: () -> Void
    @Delegated1 var didUpdate: (String) -> Void
    @Delegated2 var didReplace: (String, String) -> Void
}

开箱即用地提供了 Delegated0 - Delegated4DelegatedDelegated1 的类型别名。

注册为委托

// somewhere inside init() or viewDidLoad() or similar
self.textField = TextField()
textField.$didUpdate.delegate(to: self) { (self, text) in
    self.label.text = text
}

默认情况下,delegate(to:with:) 会将 self 包装在一个弱引用中,这样您就不需要每次都记住编写 [weak self]。 这是 Delegated 的主要功能,但如果您不希望这样,可以使用 manuallyDelegate(with:)

// somewhere inside init() or viewDidLoad() or similar
self.textField = TextField()
textField.$didUpdate.manuallyDelegate { (text) in
    print(text)
}

调用委托

final class TextField {
    @Delegated var didUpdate: (String) -> Void
    
    /// ...
    
    private func didFinishEditing() {
        self.didUpdate(self.text)
    }
}

委托一个带有返回值的函数

如果您的委托函数旨在具有返回值(非 Void),请使用 @ReturningDelegated 包装器。

final class TextField {
    @ReturningDelegated  var shouldReturn: (String) -> Bool?
    
    @ReturningDelegated0 var shouldBeginEditing: () -> Bool?
    @ReturningDelegated2 var shouldReplace: (String, String) -> Bool?
}

// ...

textField.$shouldReturn.delegate(to: self) { (self, string) -> Bool in
    if string.count > 5 {
        return true
    } else {
        return false
    }
}

重要提示:确保您的 @ReturningDelegated 函数返回一个可选值。 如果没有设置委托,它将返回 nil

默认的 @ReturningDelegated 支持正好一个输入参数。 如果您需要不同数量的参数,请使用 @ReturningDelegated0 - @ReturningDelegated4(参见上文)。

移除委托

@Delegated var didUpdate: (String) -> ()
// ...
self.$didUpdate.removeDelegate()

安装

Swift 包管理器

Delegated 仅通过 Swift 包管理器正式提供。

在 Xcode 11 或更高版本中,在您的项目中,选择:File > Swift Packages > Add Pacakage Dependency

在搜索栏中输入

https://github.com/dreymonde/Delegated

当您找到该包时,可以使用 next 按钮继续安装。

如果您在 Swift Packages 面板中找不到任何内容,您可能尚未添加您的 github 帐户。 您可以在 Xcode 的 Preferences 面板中的 Accounts 部分执行此操作。

对于基于命令行的应用程序,您可以直接将其添加到您的 Package.swift 文件中

dependencies: [
    .package(url: "https://github.com/dreymonde/Delegated", from: "2.1.0"),
]

手动

当然,您可以选择复制和粘贴代码 - Delegated 只是一个文件,所以请随意。