[self]是一个新的术语,我们可以在块中使用它来避免使用self关键字。那么它与[weak self]有什么不同?[self]是否会考虑保留周期?我找不到太多的信息,所以任何简单的例子与解释将不胜感激。
[self]
[weak self]
5q4ezhmt1#
[self]指示self被有意地与强引用一起保持(因此某些语法被简化)。[weak self]指示self被与弱引用一起保持。为什么我会使用强大的捕获[self]内块,因为有内存泄漏的机会当您知道没有引用循环,或者希望有一个临时引用循环时,可以使用此函数。捕获self本身不会创建循环。必须有一个 cycle。您可能从代码中知道没有。例如,持有闭包的东西可能被其他对象持有(而不是self)。良好的组合(以及将复杂类型分解为更小的类型)可以很容易地导致这种情况。或者,你可能需要一个临时的周期。这是URLSessionTask最常见的例子。这里的文档非常有价值(着重号是加上去的):创建任务后,可以通过调用它的resume()方法来启动它。然后,会话将保持对任务的强引用**,直到请求完成或失败**;除非对应用的内部簿记有用,否则无需维护对任务的引用。另一个常见的例子是DispatchQueue,它类似地持有一个闭包直到它完成。在这一点上,它释放它,杀死循环,并允许所有的东西释放。这是有用的和强大的(和常见的!),当使用意图。它是一个错误的来源,当不小心使用。所以Swift要求你陈述你的意图,并试图使情况明确。当您构建自己的类型来保留完成处理程序时,您也应该强烈考虑这种模式。在调用完成处理程序后,将其设置为nil(或{_ in }(非可选))以释放完成处理程序可能引用的任何内容。当前情况的一个令人沮丧的影响是开发人员不假思索地将[weak self]添加到闭包中。这与预期正好相反。看到self应该会让开发人员停下来思考引用图。我不确定它是否真的实现了这一点,但作为一名Swift程序员,你应该理解这是意图。这不仅仅是随机语法。
self
nil
{_ in }
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的引用的性质就变得无关紧要了。考虑:
.task {…}
struct ContentView: View { var body: some View { VStack { … } .task { await fetchSomeDataForThisView() } } }
当ContentView被解除时,.task { … }也被取消,并且,通过结构化并发的奇迹,当一个任务被取消时,它将取消传播到子任务(假设这些方法支持取消,例如使用URLSession方法data(for:delegate:)和data(from:delegate:)),在这些情况下,通过取消,减弱了弱引用与强引用的相关性。回到完成处理程序关闭代码的日子(遗憾的是,这里很少考虑取消逻辑的正确实现),许多人会愉快地让异步例程继续执行,而只考虑self的内存语义,但是现在我们有了一个具有一流取消逻辑的Swift并发系统,我们不仅应该问自己什么时候应该发布self,而是我们是否希望异步任务继续执行。
ContentView
.task { … }
URLSession
data(for:delegate:)
data(from:delegate:)
svgewumm3#
下面是一个关于何时使用weak self以及何时不使用weak self的实用方法,我将其分为两部分:分析和效果。
weak self
看看你的匿名函数(它经常被称为"闭包"),然后想一想,它是如何使用的?是遇到它时就会运行的东西,还是要存储起来供以后使用的东西?作为一个一般的简单规则,您应该 * 从不 * 编写weak self,除非您有一个匿名函数引用self * 并由self存储,* 从而导致一个保留循环,防止self在预期时消失。
好吧,但是有时候分析会让你感到困惑。如果不知道如何推理,就试着不使用weak self,自己看看效果是什么。具体方法如下:很简单,只要在self上实现deinit,然后记录(打印到控制台,或使用Logger)。现在练习应用程序。尝试运行闭包的场景和(如果有的话)。如果在预期self被销毁时没有看到deinit日志消息,然后,也只有到那时,您才可以开始担心可能需要weak。
deinit
weak
3条答案
按热度按时间5q4ezhmt1#
[self]
指示self
被有意地与强引用一起保持(因此某些语法被简化)。[weak self]
指示self
被与弱引用一起保持。为什么我会使用强大的捕获
[self]
内块,因为有内存泄漏的机会当您知道没有引用循环,或者希望有一个临时引用循环时,可以使用此函数。捕获
self
本身不会创建循环。必须有一个 cycle。您可能从代码中知道没有。例如,持有闭包的东西可能被其他对象持有(而不是self
)。良好的组合(以及将复杂类型分解为更小的类型)可以很容易地导致这种情况。或者,你可能需要一个临时的周期。这是URLSessionTask最常见的例子。这里的文档非常有价值(着重号是加上去的):
创建任务后,可以通过调用它的resume()方法来启动它。然后,会话将保持对任务的强引用**,直到请求完成或失败**;除非对应用的内部簿记有用,否则无需维护对任务的引用。
另一个常见的例子是DispatchQueue,它类似地持有一个闭包直到它完成。在这一点上,它释放它,杀死循环,并允许所有的东西释放。这是有用的和强大的(和常见的!),当使用意图。它是一个错误的来源,当不小心使用。所以Swift要求你陈述你的意图,并试图使情况明确。
当您构建自己的类型来保留完成处理程序时,您也应该强烈考虑这种模式。在调用完成处理程序后,将其设置为
nil
(或{_ in }
(非可选))以释放完成处理程序可能引用的任何内容。当前情况的一个令人沮丧的影响是开发人员不假思索地将
[weak self]
添加到闭包中。这与预期正好相反。看到self
应该会让开发人员停下来思考引用图。我不确定它是否真的实现了这一点,但作为一名Swift程序员,你应该理解这是意图。这不仅仅是随机语法。3pvhb19x2#
Rob Napier非常好地回答了内存语义问题(+1)。
但是,恕我直言,这只是等式的一部分。考虑一下(由question提示)下面的问题。应该使用
[weak self]
吗?或者简单地说:
恕我直言,关于
[weak self]
捕获列表的担忧往往是错误的问题。更广泛、更重要的问题是,当不再需要self
时,您想做什么。[weak self]
有效地说"让此任务继续执行,但是将self
设置为nil
"。但是如果不再需要提取数据的对象,为什么还要继续让任务执行呢?!当然,有时您希望它继续执行(例如,将某些结果保存到持久存储)。但通常,您不会(例如,它仅仅获取数据以呈现在当前视图中)。在后一种情况下,优选的图案(如SwiftUI的
.task {…}
修改器所采用的)是"当不再需要此视图时取消此任务"。如果在视图关闭时取消任务,那么对self
的引用的性质就变得无关紧要了。考虑:
当
ContentView
被解除时,.task { … }
也被取消,并且,通过结构化并发的奇迹,当一个任务被取消时,它将取消传播到子任务(假设这些方法支持取消,例如使用URLSession
方法data(for:delegate:)
和data(from:delegate:)
),在这些情况下,通过取消,减弱了弱引用与强引用的相关性。回到完成处理程序关闭代码的日子(遗憾的是,这里很少考虑取消逻辑的正确实现),许多人会愉快地让异步例程继续执行,而只考虑
self
的内存语义,但是现在我们有了一个具有一流取消逻辑的Swift并发系统,我们不仅应该问自己什么时候应该发布self
,而是我们是否希望异步任务继续执行。svgewumm3#
下面是一个关于何时使用
weak self
以及何时不使用weak self
的实用方法,我将其分为两部分:分析和效果。分析
看看你的匿名函数(它经常被称为"闭包"),然后想一想,它是如何使用的?是遇到它时就会运行的东西,还是要存储起来供以后使用的东西?
作为一个一般的简单规则,您应该 * 从不 * 编写
weak self
,除非您有一个匿名函数引用self
* 并由self
存储,* 从而导致一个保留循环,防止self
在预期时消失。效果
好吧,但是有时候分析会让你感到困惑。如果不知道如何推理,就试着不使用
weak self
,自己看看效果是什么。具体方法如下:很简单,只要在
self
上实现deinit
,然后记录(打印到控制台,或使用Logger)。现在练习应用程序。尝试运行闭包的场景和(如果有的话)。如果在预期self
被销毁时没有看到deinit
日志消息,然后,也只有到那时,您才可以开始担心可能需要weak
。