Weakify

codecov.io License

工作原理

Weakify 是一个 µframework,提供了一些常用的 weakify() 函数的变体。weakify() 主要用于在一个类中使用一个方法作为“闭包”值,该值由其他组件管理,但以一种防止内存泄漏的方式进行。

如果你定义一个(可以说是人为设计的)像这样的类

class Thing {
    func doSomething() {
        print("Something!")
    }

    var callback: () -> Void = {}

    func registerCallback() {
        callback = self.doSomething
    }
}

let thing = Thing()
thing.registerCallback()

你将创建一个 retain cycle,并且 thing 永远不会被释放。每当你引用对象上的方法而不调用它时,绑定到该方法的类的实例将被该方法捕获,并在该方法的生命周期内保持。这是因为在 Swift 中,实例方法实际上是柯里化函数:你在类和实例上编写的实际方法会闭包(强烈地)引用 self,以便确保这些引用在方法的生命周期内都存在。

你可以通过在 registerCallback 方法中执行以下操作来解决这个问题

func registerCallback() {
	callback = { [weak self] in
		self?.doSomething()
	}
}

这打破了 retain cycle。 但是,如果你要调用的方法的签名相同,则每次要这样做时都必须创建一个新的闭包,这有点麻烦,这就是 weakify() 的用武之地。 使用它,你可以像这样重写此方法

func registerCallback() {
	callback = weakify(self, type(of: self).doSomething)
}

weakify() 使用静态方法引用将对象的实例与方法分离(你可以使用 Thing.doSomethingtype(of: self).doSomething 静态地引用 doSomething 方法,其类型为 (Thing) -> () -> ())。 在此示例中,weakify 弱地将 self 应用于柯里化函数的第一个参数,返回一个类型为 () -> () 的闭包,该闭包在被调用时,仅当 self 尚未被释放时才会执行 doSomething 方法(很像先前定义的手动弱捕获 self 的闭包)。

用法

此库中有几个 weakify 变体可供您使用

func weakify <T: AnyObject, U>(_ owner: T, _ f: (T) -> () -> ()) -> (U) -> ()
func weakify <T: AnyObject, U>(_ owner: T, _ f: (T) -> () throws ->()) -> (U) throws -> ()

可以应用于任何不接受参数且不返回任何值的方法。 生成的闭包可以接受一个将被忽略的参数(在 NSNotificationCenter 之类的情况下很有用,当你不在乎 notification 参数时),或者该类型也可以表示 Void,这意味着不需要输入参数。

func weakify <T: AnyObject, U>(_ owner: T, _ f: (T) -> (U) -> ()) -> (U) -> ()
func weakify <T: AnyObject, U>(_ owner: T, _ f: (T) -> (U) throws ->()) -> (U) throws -> ()

可以应用于接受一个参数且不返回任何值的方法,生成的闭包会镜像该方法。

func weakify <T: AnyObject, U, V>(_ owner: T, _ f: (T) -> (U) -> V) -> (U) -> V?
func weakify <T: AnyObject, U, V>(_ owner: T, _ f: (T) -> (U) throws -> V) -> (U) throws -> V?

可以应用于接受和返回某些内容的函数; 实际上是前两种情况的并集。

func weakify <T: AnyObject, U, V>(_ owner: T, _ f: (T) -> (U?) -> ()) -> (V) -> ()
func weakify <T: AnyObject, U, V>(_ owner: T, _ f: (T) -> (U?) throws -> ()) -> (V) throws -> ()

可以应用于接受可选值的函数。 生成的闭包可以具有完全不同的输入参数类型。 如果在调用时 owner 不为 nil,则生成闭包的参数将使用 as? 运算符有条件地从 V 强制转换为 U,并将该结果传递给原始函数(这就是为什么它必须接受可选值,以防强制转换失败)。

要求

安装

CocoaPods

Weakify 可通过 CocoaPods 获得。 要安装它,只需将以下行添加到您的 Podfile 中

# Swift 3.x:
pod "Weakify", "~> 0.4.0"

# Swift 2.x:
pod "Weakify", "~> 0.2.3"

# Swift 1.2:
pod "Weakify", "~> 0.1.3"

Carthage

Weakify 可以与 Carthage 集成。 将以下内容添加到您的 Cartfile 中以使用它

# Swift 3:
github "klundberg/Weakify" ~> 0.4.0

# Swift 2:
github "klundberg/Weakify" ~> 0.2.3

# Swift 1.2:
github "klundberg/Weakify" ~> 0.1.3

Swift Package Manager

将以下行添加到您的 Package.swift 文件中的依赖项列表中(根据您的目标 Swift 版本更改版本)

.Package(url: "https://github.com/klundberg/weakify.git", versions:Version(0,4,0)..<Version(0,5,0)),

手动安装

如果您不能使用 CocoaPods(例如,如果您仍然需要以 iOS 7 为最低目标),则推荐的安装方法是简单地手动将 weakify.swift 从 repo 复制到您的项目中。 您也可以选择将此 repo 作为 git submodule 引用,这是一个我留给您的练习。

作者

Kevin Lundberg, kevin at klundberg dot com

贡献

如果您希望看到 Weakify 的其他变体,请随时提交 pull request! 请在任何更改中包含单元测试。

许可证

Weakify 在 MIT 许可证下可用。 有关更多信息,请参见 LICENSE 文件。