应该在Swift中调用API时使用[weak self]吗?

huangapple go评论61阅读模式
英文:

Should I use [weak self] when calling an API in Swift

问题

I'm not sure if I should use [weak self] when calling an API in Swift.
我不确定在Swift中调用API时是否应该使用[weak self]。

I'm creating a user with Firebase, and if the user is created successfully, I want to use self to perform a segue.
我正在使用Firebase创建用户,如果用户创建成功,我想使用self执行一个segue。

This is my code:
这是我的代码:

import UIKit
import FirebaseAuth

class RegisterViewController: UIViewController {

@IBOutlet weak var emailTextfield: UITextField!
@IBOutlet weak var passwordTextfield: UITextField!

@IBAction func registerPressed(_ sender: UIButton) {
if let email = emailTextfield.text, let password = passwordTextfield.text {
Auth.auth().createUser(withEmail: email, password: password) { authResult, error in
if let e = error {
print(e)
} else {
self.performSegue(withIdentifier: "registerToChat", sender: self)
}
}
}
}
}

The code is working, but I'm not sure what would happen if the Firebase API request doesn't work or is delayed for a long time, and the self pointer of the RegisterViewController doesn't get deallocated.
代码正常工作,但我不确定如果Firebase API请求失败或延迟很长时间会发生什么情况,以及RegisterViewController的self指针是否会被释放。

Therefore, I would also like to discuss the general practice of using APIs that may experience delays, and whether it is advisable to always use [weak self] in such cases.
因此,我还想讨论使用可能会延迟的API的一般做法,以及在这种情况下是否建议始终使用[weak self]。

英文:

I'm not sure if I should use [weak self] when calling an API in Swift.
I'm creating a user with Firebase, and if the user is created successfully, I want to use self to perform a segue.

This is my code:

import UIKit 
import FirebaseAuth

class RegisterViewController: UIViewController {

  @IBOutlet weak var emailTextfield: UITextField!
  @IBOutlet weak var passwordTextfield: UITextField!

  @IBAction func registerPressed(_ sender: UIButton) {
    if let email = emailTextfield.text, let password = passwordTextfield.text {
        Auth.auth().createUser(withEmail: email, password: password) { authResult, error in
            if let e = error {
                print(e)
            } else {
                self.performSegue(withIdentifier: "registerToChat", sender: self)
            }
        }
    }
  }}

The code is working, but I'm not sure what would happen if the Firebase API request doesn't work or is delayed for a long time, and the self pointer of the RegisterViewController doesn't get deallocated.
Therefore, I would also like to discuss the general practice of using APIs that may experience delays, and whether it is advisable to always use [weak self] in such cases.

答案1

得分: 4

在你的示例中,几乎肯定会使用对self的弱引用。如果视图控制器已被解散,执行segue将毫无意义。虽然在这里没有持久的强引用循环风险,但没有理由将该视图控制器保留在内存中比需要的时间更长。使用weak引用允许视图控制器在不再需要时被销毁,而不必等待异步调用完成。使用weak引用可以是一种良好的防御性编程模式。

希望这回答了关于你具体问题的问题,但更普遍地说,有一些考虑因素:

  1. 在这里不是一个问题,但我们通常会使用weak引用来消除持久的强引用循环的可能性(在那些可能将闭包保存在某些属性中并且API服务在完成时忽略释放此闭包的情况下)。这通常不是成熟API(如Firebase)的问题,但在自制API或服务中可能不会注意这些细节的情况下,这变得更加重要。我们通常会在不太确定库在内存语义方面的纪律性如何时,防御性地使用weak引用。

  2. 当处理不是视图控制器的调用者时(通常情况下,视图控制器应在相关视图被解散后立即释放;对于某些服务类可能不适用相同的规则),会引发是否希望保持强引用直到请求完成的问题。

    例如,按设计,如果你在URLSession中使用代理,它会保持对其代理的强引用直到会话被无效化。其他服务可能不会这样做。无论如何,确实存在一些用例,即使视图已被解散或未解散,调用者可能都希望将某些异步请求的结果保存在某个模型对象或缓存中。在这种边缘情况下,你可能希望在请求完成之前有一个临时的对self的强引用。

  3. 尽管你关注内存语义,但通常更广泛的问题是“当调用者被销毁时应该发生什么”。具体来说,在处理可取消的API时,我们不是担心调用者的生命周期,而是关注被调用的API的生命周期。具体来说,我们可能会在不再需要其结果的情况下明确取消请求。在这种情况下,你不仅会释放self的资源,还会释放API服务的资源。

    现在,在你的示例中,Auth可能不支持取消。如果不支持,这个问题就没有意义。但在API支持取消的情况下,与其纠结于“我应该保持强引用直到请求完成”,不如关注“如果在异步请求完成之前解散了调用者,我是否应该立即停止/取消异步API?”。

所以,“我应该始终使用weak引用”这个问题的简短答案是,“通常是这样,但这取决于情况。”通常情况下,我们会倾向于使用weak引用,但还有很多用例,我们可能会有意地使用强引用或追求主动取消的模式。

英文:

In your example, you almost certainly would use weak reference to self. It would make no sense to perform the segue if the view controller had been dismissed. And while there is no persistent strong reference cycle risk here, there simply is no reason to keep this view controller in memory longer than is needed. Using weak reference allows the view controller to be deallocated as soon as it is no longer needed, and it will not have to wait for the asynchronous call to complete. Using weak references can be a good defensive programming pattern.

Hopefully that answers the question regarding your specific question, but more generally, there are a number of considerations:

  1. It is not a concern here, but we will often use weak references to eliminate the possibility of persistent strong reference cycles (in those cases where the closure might be saved in some property and the API service neglects to release this closure when it was done). This is generally not a concern in mature API, like Firebase, but becomes more of a concern in home-spun API or services, which might not pay attention to these sorts of details. We often will use weak references in a defensive manner when we might not be entirely sure how disciplined the library might be with respect to its memory semantics.

  2. When dealing with a caller that is something other than a view controller (view controllers, as a rule, should be released as soon as the associated view is dismissed; the same might not be true for some service class), it raises whether you want to keep a strong reference until the request finishes or not.

    E.g., by design, if you use delegates with URLSession, it keeps a strong reference to its delegate until the session is invalidated. Other services might not do that. Regardless, there are definitely use-cases where, having started some asynchronous request, the caller might want to save the results in some model object or cache, regardless of whether the view was dismissed or not. In edge cases like this, you might consciously want a temporary strong reference to self until the request finishes.

  3. While you are focusing on memory semantics, often the broader question is “what should happen when the caller is deallocated”. Specifically, when dealing with cancelable API, rather than worrying about the lifecycle of the caller, we instead focus on the lifecycle of the called API. Specifically, we might explicitly cancel the request as soon as its results are no longer needed. In that scenario, you not only free the resources of self, but also those of the API service, too.

    Now, in your example, the Auth may not support cancelation. If not, this issue is moot. But in cases where the API does support cancelation, rather than dwelling on “should I keep a strong reference until the request is done”, you should focus on “if the caller is dismissed before the asynchronous request finishes, should I just immediately stop/cancel the asynchronous API?”

So, the short answer to “should I always use weak references” is, “often, but it depends.” Generally we gravitate to weak references, but there are lots of use-cases where we might consciously use strong references or pursue proactive cancelation patterns.


There is a final, loosely related question, unrelated to the memory semantics. Namely, what you may want to have happen if the user completely leaves the app while the asynchronous request is in progress? Sometimes (especially with uploads, post requests, etc.), the app may want to ask the OS for a little extra time in case the user leaves the app while the request is underway. E.g., in iOS, we might Continue Foreground Work in the Background.

答案2

得分: -1

你应该在闭包内部使用 [weak self],以避免产生强引用并防止引用循环。你可以查看这份文档了解我们是否应该始终在闭包内部使用 [weak self]

参见 You don’t (always) need [weak self]Understanding Weak and Unowned References in Swift Closures

在进行 API 调用时,使用 [weak self] 或执行异步操作是一个良好的实践,可以防止强引用循环和潜在的内存泄漏。

英文:

You should use [weak self] inside a Closures to avoid giving the strong reference and avoid the reference cycle. You can see this documentation to understand whether we should use [weak self] inside closures all the time or not.

See You don’t (always) need [weak self] or Understanding Weak and Unowned References in Swift Closures.

And for API calling it is a good practice to use [weak self] or perform asynchronous operations to prevent strong reference cycles and potential memory leaks.

huangapple
  • 本文由 发表于 2023年5月21日 03:03:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/76296918.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定