避免在 DispatchQueue 中使用 self
Avoiding using self in the DispatchQueue
我想知道如何消除在 DispatchQueue
中使用 self
。作为一种好的做法,我们应该只在 init()
中使用 self
func loadAllClasses() {
DispatchQueue.global(qos: .background).async {
self.classVM.fetchAllClasses(id: id, completion: { (classes, error) in
DispatchQueue.main.async {
if error != nil {
self.showAlert(message: "try again", title: "Error")
}
if let classes = classes {
self.classesList = classes
self.classesCollectionView.reloadData()
}
}
})
}
}
别担心! DispatchQueue
闭包不会导致保留循环。
“作为一种好的做法,我们应该只在 init() 中使用 self”
这当然不是真的。事实上,您使用 self
的次数比您在代码中看到的要多,因为这是编译器在您使用成员变量或成员函数时设置的隐式参数。基本上,您将无法按照此规则编写任何“真正的”面向对象代码。
此外,在 init
函数中使用 self
甚至 特殊 - 因为 self
是对“ under construction”,甚至在值还没有完全初始化的情况下,甚至不允许使用self
。
所以,话虽如此,在将要分派的闭包中使用 self
是完全不同的事情。
强攻self
:
给定一个在闭包中使用 self
的成员函数,如下所示:
func loadAllClasses() {
queue.async {
self.foo =
}
}
在这里,您捕获了对 self
的强引用,这将使 self
引用的对象至少在闭包被调用和完成之前保持活动状态。
我们可以将其称为“临时”保留周期 - 但假设完成处理程序将被调用 最终 闭包将被释放,并且强引用将被释放 self
还有。
所以,这会自动解决 - 我们不必担心......,除了我们犯了一些程序员错误,其中不会调用完成处理程序(不过这是另一章;))
弱捕获self
:
如果需要,您可以阻止闭包使对象保持活动状态 - 通过弱捕获 self
。这是你必须做出的决定,在某些情况下你可能希望让对象保持活动状态,而在其他情况下你不希望对象在关闭完成之前一直处于活动状态.
func loadAllClasses() {
queue.async { [weak self] in
self?.foo =
}
}
这里,闭包只有对 self
的弱引用,因此,它不会让对象保持活动状态,直到它被调用为止。在闭包中,您需要获得对 weak self 的临时强引用才能使用它。注意闭包执行的时候可能是nil
使用一些其他技巧:
有时您需要 self
的 属性 中的特定值 - 但您不太关心 self
本身:
func loadAllClasses() {
let foo = self.foo
queue.async {
let bar = foo
}
}
在这里,你完全避免捕获self
。你可以使用这个,当你可以说当你创建闭包时所有值都是已知的并且当你稍后在某个时间点执行闭包时它们的原始值的变化无关紧要。
你问过:
I wonder how to eliminate of using self
inside the DispatchQueue
.
在 Swift 5.3 中采用的 SE-0269 中,他们引入了一种模式,如果您在捕获列表中包含 self
,则不需要所有 self
闭包中的引用:
func loadAllClasses() {
DispatchQueue.global(qos: .background).async { [self] in // note [self] capture list
classVM.fetchAllClasses(id: id) { (classes, error) in
DispatchQueue.main.async {
if error != nil {
showAlert(message: "try again", title: "Error")
}
if let classes = classes {
classesList = classes
classesCollectionView.reloadData()
}
}
}
}
}
顺便说一句,fetchAllClasses
似乎已经是异步的,因此不需要向全局队列发送:
func loadAllClasses() {
classVM.fetchAllClasses(id: id) { [self] (classes, error) in // note [self] capture list
DispatchQueue.main.async {
if error != nil {
showAlert(message: "try again", title: "Error")
}
if let classes = classes {
classesList = classes
classesCollectionView.reloadData()
}
}
}
}
As a good practice, we are supposed to use self
only in the init()
?
不,只要可以消除歧义或需要明确潜在的强引用循环,就可以使用 self
。不要回避使用 self
引用。
但是直觉,消除不必要的 self
引用是好的,因为在不需要的地方,它最终会成为句法噪音。如果您的更广泛的代码库到处都是 self
引用,那么在闭包中要求 self
引用的全部意义就被破坏了。
顺便说一句,上面我说明了你愿意捕获self
的地方,你可以使用[self] in
语法使自己的代码更简洁,消除很多不必要的self
闭包内的引用。
话虽如此,在这种情况下我们通常希望使用 [weak self]
引用。当然,正如 vadian 所建议的那样,可能没有任何强大的参考循环风险。但这是所讨论对象的所需生命周期的问题。例如,让我们暂时假设 fetchAllClasses
可能非常慢。让我们进一步假设用户可能想要关闭有问题的视图控制器。
在那种情况下,您真的想让视图控制器保持活动状态以完成 fetchAllClasses
,其唯一目的是更新已被关闭的集合视图吗?!?应该不是。
我们中的许多人在调用可能较慢的异步进程时会使用 [weak self]
:
func loadAllClasses() {
classVM.fetchAllClasses(id: id) { [weak self] (classes, error) in // note `[weak self]` so we don't keep strong reference to VC longer than we need
DispatchQueue.main.async {
guard let self = self else { return } // if view controller has been dismissed, no further action is needed
guard error == nil, let classes = classes else { // handle error and unwrap classes in one step
self.showAlert(message: "try again", title: "Error")
return
}
self.classesList = classes // otherwise, proceed as normal
self.classesCollectionView.reloadData()
}
}
}
是的,这重新引入了我们上面删除的 self
引用,但这是异步请求的一个很好的模式,一般来说:我们从不让一些旨在更新视图控制器的网络请求阻止该视图控制器不会在用户关闭时被释放。
上述模式的进一步改进是将 fetchAllClasses
设计为可取消的,然后在视图控制器的 deinit
中取消任何未决的网络请求(如果有)。这超出了这个问题的范围,但我们的想法是,我们不仅不应该保留视图控制器超过必要的时间,而且我们也应该取消挂起的请求。但是,这种 deinit
取消模式仅在您在闭包中使用 weak
引用时才有效。
我想知道如何消除在 DispatchQueue
中使用 self
。作为一种好的做法,我们应该只在 init()
self
func loadAllClasses() {
DispatchQueue.global(qos: .background).async {
self.classVM.fetchAllClasses(id: id, completion: { (classes, error) in
DispatchQueue.main.async {
if error != nil {
self.showAlert(message: "try again", title: "Error")
}
if let classes = classes {
self.classesList = classes
self.classesCollectionView.reloadData()
}
}
})
}
}
别担心! DispatchQueue
闭包不会导致保留循环。
“作为一种好的做法,我们应该只在 init() 中使用 self”
这当然不是真的。事实上,您使用 self
的次数比您在代码中看到的要多,因为这是编译器在您使用成员变量或成员函数时设置的隐式参数。基本上,您将无法按照此规则编写任何“真正的”面向对象代码。
此外,在 init
函数中使用 self
甚至 特殊 - 因为 self
是对“ under construction”,甚至在值还没有完全初始化的情况下,甚至不允许使用self
。
所以,话虽如此,在将要分派的闭包中使用 self
是完全不同的事情。
强攻self
:
给定一个在闭包中使用 self
的成员函数,如下所示:
func loadAllClasses() {
queue.async {
self.foo =
}
}
在这里,您捕获了对 self
的强引用,这将使 self
引用的对象至少在闭包被调用和完成之前保持活动状态。
我们可以将其称为“临时”保留周期 - 但假设完成处理程序将被调用 最终 闭包将被释放,并且强引用将被释放 self
还有。
所以,这会自动解决 - 我们不必担心......,除了我们犯了一些程序员错误,其中不会调用完成处理程序(不过这是另一章;))
弱捕获self
:
如果需要,您可以阻止闭包使对象保持活动状态 - 通过弱捕获 self
。这是你必须做出的决定,在某些情况下你可能希望让对象保持活动状态,而在其他情况下你不希望对象在关闭完成之前一直处于活动状态.
func loadAllClasses() {
queue.async { [weak self] in
self?.foo =
}
}
这里,闭包只有对 self
的弱引用,因此,它不会让对象保持活动状态,直到它被调用为止。在闭包中,您需要获得对 weak self 的临时强引用才能使用它。注意闭包执行的时候可能是nil
使用一些其他技巧:
有时您需要 self
的 属性 中的特定值 - 但您不太关心 self
本身:
func loadAllClasses() {
let foo = self.foo
queue.async {
let bar = foo
}
}
在这里,你完全避免捕获self
。你可以使用这个,当你可以说当你创建闭包时所有值都是已知的并且当你稍后在某个时间点执行闭包时它们的原始值的变化无关紧要。
你问过:
I wonder how to eliminate of using
self
inside theDispatchQueue
.
在 Swift 5.3 中采用的 SE-0269 中,他们引入了一种模式,如果您在捕获列表中包含 self
,则不需要所有 self
闭包中的引用:
func loadAllClasses() {
DispatchQueue.global(qos: .background).async { [self] in // note [self] capture list
classVM.fetchAllClasses(id: id) { (classes, error) in
DispatchQueue.main.async {
if error != nil {
showAlert(message: "try again", title: "Error")
}
if let classes = classes {
classesList = classes
classesCollectionView.reloadData()
}
}
}
}
}
顺便说一句,fetchAllClasses
似乎已经是异步的,因此不需要向全局队列发送:
func loadAllClasses() {
classVM.fetchAllClasses(id: id) { [self] (classes, error) in // note [self] capture list
DispatchQueue.main.async {
if error != nil {
showAlert(message: "try again", title: "Error")
}
if let classes = classes {
classesList = classes
classesCollectionView.reloadData()
}
}
}
}
As a good practice, we are supposed to use
self
only in theinit()
?
不,只要可以消除歧义或需要明确潜在的强引用循环,就可以使用 self
。不要回避使用 self
引用。
但是直觉,消除不必要的 self
引用是好的,因为在不需要的地方,它最终会成为句法噪音。如果您的更广泛的代码库到处都是 self
引用,那么在闭包中要求 self
引用的全部意义就被破坏了。
顺便说一句,上面我说明了你愿意捕获self
的地方,你可以使用[self] in
语法使自己的代码更简洁,消除很多不必要的self
闭包内的引用。
话虽如此,在这种情况下我们通常希望使用 [weak self]
引用。当然,正如 vadian 所建议的那样,可能没有任何强大的参考循环风险。但这是所讨论对象的所需生命周期的问题。例如,让我们暂时假设 fetchAllClasses
可能非常慢。让我们进一步假设用户可能想要关闭有问题的视图控制器。
在那种情况下,您真的想让视图控制器保持活动状态以完成 fetchAllClasses
,其唯一目的是更新已被关闭的集合视图吗?!?应该不是。
我们中的许多人在调用可能较慢的异步进程时会使用 [weak self]
:
func loadAllClasses() {
classVM.fetchAllClasses(id: id) { [weak self] (classes, error) in // note `[weak self]` so we don't keep strong reference to VC longer than we need
DispatchQueue.main.async {
guard let self = self else { return } // if view controller has been dismissed, no further action is needed
guard error == nil, let classes = classes else { // handle error and unwrap classes in one step
self.showAlert(message: "try again", title: "Error")
return
}
self.classesList = classes // otherwise, proceed as normal
self.classesCollectionView.reloadData()
}
}
}
是的,这重新引入了我们上面删除的 self
引用,但这是异步请求的一个很好的模式,一般来说:我们从不让一些旨在更新视图控制器的网络请求阻止该视图控制器不会在用户关闭时被释放。
上述模式的进一步改进是将 fetchAllClasses
设计为可取消的,然后在视图控制器的 deinit
中取消任何未决的网络请求(如果有)。这超出了这个问题的范围,但我们的想法是,我们不仅不应该保留视图控制器超过必要的时间,而且我们也应该取消挂起的请求。但是,这种 deinit
取消模式仅在您在闭包中使用 weak
引用时才有效。