swift 什么时候在快速格挡中使用[自我]和[弱自我]?

jxct1oxe  于 2023-02-28  发布在  Swift
关注(0)|答案(3)|浏览(147)

[self]是一个新的术语,我们可以在块中使用它来避免使用self关键字。那么它与[weak self]有什么不同?[self]是否会考虑保留周期?
我找不到太多的信息,所以任何简单的例子与解释将不胜感激。

5q4ezhmt

5q4ezhmt1#

[self]指示self被有意地与强引用一起保持(因此某些语法被简化)。[weak self]指示self被与弱引用一起保持。
为什么我会使用强大的捕获[self]内块,因为有内存泄漏的机会
当您知道没有引用循环,或者希望有一个临时引用循环时,可以使用此函数。捕获self本身不会创建循环。必须有一个 cycle。您可能从代码中知道没有。例如,持有闭包的东西可能被其他对象持有(而不是self)。良好的组合(以及将复杂类型分解为更小的类型)可以很容易地导致这种情况。
或者,你可能需要一个临时的周期。这是URLSessionTask最常见的例子。这里的文档非常有价值(着重号是加上去的):
创建任务后,可以通过调用它的resume()方法来启动它。然后,会话将保持对任务的强引用**,直到请求完成或失败**;除非对应用的内部簿记有用,否则无需维护对任务的引用。
另一个常见的例子是DispatchQueue,它类似地持有一个闭包直到它完成。在这一点上,它释放它,杀死循环,并允许所有的东西释放。这是有用的和强大的(和常见的!),当使用意图。它是一个错误的来源,当不小心使用。所以Swift要求你陈述你的意图,并试图使情况明确。
当您构建自己的类型来保留完成处理程序时,您也应该强烈考虑这种模式。在调用完成处理程序后,将其设置为nil(或{_ in }(非可选))以释放完成处理程序可能引用的任何内容。
当前情况的一个令人沮丧的影响是开发人员不假思索地将[weak self]添加到闭包中。这与预期正好相反。看到self应该会让开发人员停下来思考引用图。我不确定它是否真的实现了这一点,但作为一名Swift程序员,你应该理解这是意图。这不仅仅是随机语法。

3pvhb19x

3pvhb19x2#

Rob Napier非常好地回答了内存语义问题(+1)。
但是,恕我直言,这只是等式的一部分。考虑一下(由question提示)下面的问题。应该使用[weak self]吗?

Task { [weak self] in
    await self?.fetchSomeDataForThisView()
}

或者简单地说:

Task {
    await self.fetchSomeDataForThisView()
}

恕我直言,关于[weak self]捕获列表的担忧往往是错误的问题。更广泛、更重要的问题是,当不再需要self时,您想做什么。[weak self]有效地说"让此任务继续执行,但是将self设置为nil "。但是如果不再需要提取数据的对象,为什么还要继续让任务执行呢?!
当然,有时您希望它继续执行(例如,将某些结果保存到持久存储)。但通常,您不会(例如,它仅仅获取数据以呈现在当前视图中)。在后一种情况下,优选的图案(如SwiftUI的.task {…}修改器所采用的)是"当不再需要此视图时取消此任务"。如果在视图关闭时取消任务,那么对self的引用的性质就变得无关紧要了。
考虑:

struct ContentView: View {
    var body: some View {
        VStack { … }
            .task {
                await fetchSomeDataForThisView()
            }
    }
}

ContentView被解除时,.task { … }也被取消,并且,通过结构化并发的奇迹,当一个任务被取消时,它将取消传播到子任务(假设这些方法支持取消,例如使用URLSession方法data(for:delegate:)data(from:delegate:)),在这些情况下,通过取消,减弱了弱引用与强引用的相关性。
回到完成处理程序关闭代码的日子(遗憾的是,这里很少考虑取消逻辑的正确实现),许多人会愉快地让异步例程继续执行,而只考虑self的内存语义,但是现在我们有了一个具有一流取消逻辑的Swift并发系统,我们不仅应该问自己什么时候应该发布self,而是我们是否希望异步任务继续执行。

svgewumm

svgewumm3#

下面是一个关于何时使用weak self以及何时不使用weak self的实用方法,我将其分为两部分:分析和效果。

分析

看看你的匿名函数(它经常被称为"闭包"),然后想一想,它是如何使用的?是遇到它时就会运行的东西,还是要存储起来供以后使用的东西?
作为一个一般的简单规则,您应该 * 从不 * 编写weak self,除非您有一个匿名函数引用self * 并由self存储,* 从而导致一个保留循环,防止self在预期时消失。

效果

好吧,但是有时候分析会让你感到困惑。如果不知道如何推理,就试着不使用weak self,自己看看效果是什么。
具体方法如下:很简单,只要在self上实现deinit,然后记录(打印到控制台,或使用Logger)。现在练习应用程序。尝试运行闭包的场景和(如果有的话)。如果在预期self被销毁时没有看到deinit日志消息,然后,也只有到那时,您才可以开始担心可能需要weak

相关问题