Avoiding [weak self] for simple operations? - ios

For short-running operations, is it acceptable to avoid [weak self]? For example, URLSession will retain the closure from dataTask(with:completion:):
final class ViewController: UIViewController {
let label = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data else { return }
let decodedString = String(bytes: data, encoding: .utf8)
DispatchQueue.main.async {
self.label.text = decodedString
}
}.resume()
}
}
In this case, the closure captures self strongly, which means even if this ViewController is held in memory by the closure. URLSession will hold the closure until the data task is complete, which means the life cycle of the ViewController can potentially be extended until dataTask completes.
In this situation, should we use capture lists to avoid this behavior? Is my reasoning correct that there's no reference cycle here?

the life cycle of the ViewController can potentially be extended until dataTask completes
So the question is whether that would be coherent. It might even be a good thing. If it would, then fine, and there’s no need for weak self, as there is no retain cycle because the
url session is shared.
But when the url session is an instance
property and has a real delegate, things are much more complicated and
you really can get a retain cycle, because the session retains its delegate which might be retaining the session.

If you are worried about reference cycles, you usually don't get one when using URL requests. The thing is that the URL request finishes sooner or later (after several minutes) and you controller gets released. The reference cycle is only temporary and it won't cause a memory leak.
The question is whether you want to keep the controller in memory even if the user has already closed the controller and it won't be ever shown again. It won't probably cause any problems but it's still wasteful. You are holding to memory you don't need and that cannot be reused.
Also note that you might actually want to cancel the running request when the controller is dismissed to avoid sending/receiving data that is not necessary anymore.
In my opinion, you shouldn't worry that much about reference cycles and think more about ownership. A strong reference means that something is owned. The request has no reason to "own" the controller. It's the other way around - the controller owns and manages the request. If there is no ownership, I would use weak just for clarity.

Is my reasoning correct that there's no reference cycle here?
There is no reference cycle here. ViewController is not retaining the dataTask completion handler. You can think of this as iOS keeping a strong reference to both the view controller and the completion handler, and the completion handler also keeping a strong reference to the view controller. There is no strong reference from the view controller back to the completion handler, or to any chain of objects with a reference to the completion handler, so you are cycle-free. Look for this same pattern in UIView.animate, where you are again sending closures to iOS instead of storing them locally.
For short-running operations, is it acceptable to avoid [weak self]?
The duration of the work is not a factor. The two pertinent questions are:
Does a cycle of references exist?
Will the cycle of references be broken?
Take this example:
class BadVC: UIViewController {
private lazy var cycleMaker: () -> Void = { print(self) }
override func loadView() {
view = UIView()
cycleMaker()
}
}
BadVC here manages to create a reference cycle that will never be broken as soon as it loads its view. The fact that cycleMaker() will execute in nanoseconds does not save us from a memory leak.
Pragmatically, there is a third question:
Does this code avoid permanent reference cycles in a way that is difficult to understand, easy to break, or unreliable, so reference cycles are likely to emerge in the future due to misuse or modification?
You can break reference cycles manually. For example:
class StillBadVC: UIViewController {
private lazy var cycleMaker: () -> Void = { print(self) }
override func loadView() {
view = UIView()
cycleMaker()
}
func breakCycle() {
cycleMaker = { }
}
}
Here, we are in danger because StillBadVC has a strong reference to cycleMaker and cycleMaker captures a strong reference to StillBadVC. The cycle will be broken as long as someone remembers to call breakCycle(), at which point the view controller will remove its strong reference to cycleMaker, allowing cycleMaker to deallocate. However, the cycle will not be broken if someone forgets to call breakCycle(). Calling a method called breakCycle() is not usually a part of the contract for using a view controller, so we would expect StillBadVC to result in memory leaks in practice.

You definitely should use [weak self] here, not because of any risk of strong reference cycle, but simply because this closure exists solely to update a label. There is no point in writing code that deliberately keeps the view controller and its views in memory so you can update a label in a view that may have been dismissed and is no longer visible.
The weak keyword does not exist solely to avoid strong reference cycles, but rather to accurately represent the object ownership and manage object lifespans. You shouldn’t misrepresent the object ownership graph just for the sake of saving the few keystrokes associated with [weak self] capture list.

I think you've already got your answer here. It's not a reference cycle.
But to build a systematic approach, my advice here is one more simpler. Forget thinking about leaks and stuff.
Think about ownership and flow control and then memory-management
Ownership: Does this object need to own this other object? Should an object own its delegate? Should a subview own its parentiew? Does this request own this viewController?
Flow Control: How soon do I want to de-allocate this object? Immediately or upon the view being removed from the screen?
Memory-management: Is this a strong reference cycle?
This thought process not only helps you distinguish a true memory leak from something that isn't a leak. It also helps you design and read your code better and not just slavishly dump [weak self] all over the place.

Related

Is it possible that UI view item no longer valid, after we finish executing DispatchWorkItem and attempt to update UI with DispatchQueue.main.async

Given the following code
#IBAction func buttonClick(_ sender: Any) {
recordLabel.text = "Perform some time consuming networking..."
let workItem = DispatchWorkItem {
// Perform some time consuming networking...
DispatchQueue.main.async {
// QUESTION: Is it possible, that the UI recordLabel is
// destroy/ invalid at this stage?
self.recordLabel.text = "Done"
}
}
DispatchQueue.global().async(execute: workItem)
}
which is
Perform time consuming job (Like networking) in user thread
Update UI in UI thread after finishing time consuming job
I was wondering, during UI updating, is there ever be a chance, that the UI is destroy, or no longer valid?
The reason is that, for Android ecosystem, they have tendency to "re-create" UI in the middle of user thread execution (See Best practice: AsyncTask during orientation change). This make the UI reference hold by the user thread no longer valid.
I was wondering, does iOS Swift exhibits similar behavior?
I was wondering, during UI updating, is there ever be a chance, that the UI is destroy, or no longer valid?
If the view controller in question has been dismissed, the view will be removed from the view hierarchy. But, the way you’ve written this, if the view controller has been dismissed, your code will update a view that isn’t visible any more. Worse, the memory associated with your view controller and its views be not deallocated until this dispatched block finishes. There’s no point in doing that.
So, if we were to use your code pattern, you might instead do:
#IBAction func buttonClick(_ sender: Any) {
recordLabel.text = "Perform some time consuming networking..."
let workItem = DispatchWorkItem { // use `[weak self] in` pattern here, too, if you reference `self` anywhere
// Perform some time consuming networking...
DispatchQueue.main.async { [weak self] in
// Update the view if it’s still around, but don’t if not
self?.recordLabel.text = "Done"
}
}
DispatchQueue.global().async(execute: workItem)
}
Or, more naturally,
#IBAction func buttonClick(_ sender: Any) {
recordLabel.text = "Perform some time consuming networking..."
DispatchQueue.global().async { // use `[weak self] in` pattern here, too, if you reference `self` anywhere
// Perform some time consuming networking...
DispatchQueue.main.async { [weak self] in
// Update the view if it’s still around, but don’t if not
self?.recordLabel.text = "Done"
}
}
}
It is worth noting that one generally doesn’t dispatch network requests to a global queue because networking libraries like URLSession, Alamofire, etc., already perform their request asynchronously. So you wouldn’t do that async dispatch to the global queue.
Likewise, if this network request was merely to update something for this view controller’s view, you might even cancel the network request when the view was dismissed. (Why bother continuing to do a network request merely to update a view that might no longer exist?) It depends upon the nature of the request.
Finally, when you get this immediate issue behind you, you might reconsider whether the view controller should be issuing network requests at all, as opposed to some other type of object. That’s well beyond the scope of this question, but something to reconsider in the long term.
But we can’t comment further on any of these observations without seeing what you’re doing inside this dispatch to the global queue.
UI will not be deallocated until your view controller deallocs . When you create closure of DispatchWorkItem and Dispatched main queue you retain the self inside. so it will increase the retain count of your view controller. so until your retain count of view controller is not getting 0 view controller will not be deallocated as view (IBOutlet).
Now If you add [unowned self] in closure and before the execution of block your view controller deallocated for whatever reason like you dismiss or pop from navigation stack you will find a crash on line self.recordLabel. because self is not retained inside the block. for [weak self] it may work fine until you not forcefully unwrap self.
Referencing self in your closure created a strong reference to that instance, which means that as long as the reference exists, the object will be alive. And the reference will exist as long as the closure that captures it is alive.
If not specified otherwise, e.g. via [weak self], or [unowned self], closures capture strongly by default any reference.
So you should be ok, memory-wise. Beware though, strongly capturing self can lead to retain cycles. Your code doesn't have this problem at this moment, but you never know how the code evolves in the future.
Another question is why you'd want to keep a controller alive for maybe a long period of time? It might happen that by the time the network calls finish, the controller is no longer on screen, thus the UI operations are in vain.
On a side note, you could directly capture recordLabel, as you only need that in your dispatch:
let workItem = DispatchWorkItem { [recordLabel]
// Perform some time consuming networking...
DispatchQueue.main.async {
recordLabel.text = "Done"
}
}
This will avoid the retain cycle, however it doesn't ensure that the controller will be kept alive, it only ensures that the label will still exist by the time the dispatch on main happens. But again, unless the controller does some extra business logic work besides updating UI, why would you need it alive for an indefinite period of time?
No, that recordLabel will not be released from memory until its ViewController get destroy.
But you should use [weak self] when you update UI from main thread if your ViewController may get destroy before networking is finished.
Theoretically it is possible that your app suspended or terminated by the system during this operation, or that the user navigates away from that view, potentially deallocating the object that owns the recordLabel (self). I would say it's good defensive practice to capture self as weak in this case.
DispatchQueue.main.async { [weak self] in
self?.recordLabel.text = "Done"
}
Generally it's better for maintainability have to have an object that abstracts the async calls, defining the work in terms of the job you want done. There are alternative APIs/patterns to DispatchQueue, when you come back to the code it will be easier to change and update if needed.

How to weak reference a function passed as a parameter

I have a strongly referenced model in my view controller that has a function listenToQueue which "listens" to my Firebase database for real time updates. Whenever an update occurs, it passes back an object to my view controller, which then calls a function. Here is my code:
eventModel.listenToQueue { [weak self] queuer in
self?.queuerDidChange(queuer: queuer)
}
This works. However, originally my code looked like this:
eventModel.listenToQueue(updateHandler: queuerDidChange)
This caused a retain cycle between the model and the view controller. However, that code looks cleaner, so I'm wondering if there's a way to pass the queuerDidChange function via a weak reference to self? I tried this already, but the retain cycle is still there:
weak var weakSelf = self
eventModel.listenToQueue(updateHandler: weakSelf?.queuerDidChange)
Is it possible to do what I want, or shall I just stick to the code I've got?
weak var weakSelf = self
eventModel.listenToQueue(updateHandler: weakSelf?.queuerDidChange)
Above code is the same code as:
eventModel.listenToQueue(updateHandler: queuerDidChange)
Because you declare weakSelf and use it immediately. weakSelf?.queuerDidChange has no relation to weakSelf, instead it already contains reference to self itself. I assume queuerDidChange member function. So the only way to avoid retain cycle is what you call the dirty way.
eventModel.listenToQueue { [weak self] queuer in
self?.queuerDidChange(queuer: queuer)
}
I noticed this in one of my apps. I like the look of passing in the method itself, but I also had to use the weak reference technique you mentioned.
This makes sense, I think, because method implementations are held on the class itself, and self is bound to that implementation when classes are instantiated. Therefore a method will always have a strong reference to self.
And in Swift, function parameters are strongly referenced. I don’t think there’s a way to pass any parameter using a weak reference.

What happens to a promise that's abandoned?

I have the following code defined in a view controller.
_ = accountService.getAccount()
.then { user -> Void in
self.txtBxUser.text = user.username
self.txtBxEmail.text = user.email
}
getAccount makes a REST API request.
If the user dismisses the view controller before the call has returned, what happens to the call back chain? Does it still run given that, I presume, it's still referenced?
If the user dismisses the view controller before the call has returned, what happens to the call back chain? Does it still run given that, I presume, it's still referenced?
Yes, it does still run.
Be forewarned that the reference to self in the closure means that it is also keeping a reference to the view controller until that then closure finishes running. For that reason, if there's a chance the view controller could have been dismissed, you might want to use a weak reference:
_ = accountService.getAccount().then { [weak self] user -> Void in
self?.txtBxUser.text = user.username
self?.txtBxEmail.text = user.email
}
Ideally, you should also make getAccount cancellable and cancel it in the view controller's deinit.
(Note, in FAQ - Should I be Concerned About Retain Cycles, the PromiseKit documentation points out that you don't need weak references, which is correct. It's just a question of whether or not you mind if the deallocation of the dismissed view controller is deferred until after the promise is fulfilled.)
Should I be concerned about retain cycles? tl;dr: it’s safe to use
self in promise handlers.
This is safe:
somePromise.then {
self.doSomething()
}
Provided somePromise resolves, the function passed to then will be
released, thus specifying [weak self] is not necessary.
Specifying [unowned self] is likely dangerous.
You’re telling me not to worry about retain cycles?! No, it’s just
that by default you are not going to cause retain cycles when using
PromiseKit. But it is still possible, for example:
self.handler = {
self.doSomething
self.doSomethingElse }
somePromise.then(execute: self.handler)
The closure we assign to
handler strongly retains self. handler is strongly retained by self.
It’s a typical retain cycle. Don’t write typical retain cycles!
Notably, this is not a retain cycle:
somePromise.then(execute: self.doSomething).then(execute: self.doSomethingElse)
source

Swift closures [weak self] and async tasks

Imagine a situation, when you want to asynchronously load some text from the server and display the result in the ViewController's UITextField.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
//... some long running async operation
if let textResponse = responseFromServer {
dispatch_async(dispatch_get_main_queue(), { [weak self] () in
self?.textField.text = textResponse
})
}
})
A.) Do I need to use [weak self] within the closure used for async calls?
I thought I need to, but I am not sure after I read some Q/A here at StackOverflow and went through quite a few open source apps that don't use [weak self] for async tasks + closures.
i.e.:
The only time where you really want to use [unowned self] or [weak
self] is when you would create a strong reference cycle.
(Shall we always use [unowned self] inside closure in Swift)
There is no strong reference cycle in my case.
or:
But to be clear, it would still be best to use a strong reference in
this circumstance. (Swift ARC and blocks)
B.) Let's say it's good to go with the strong reference. What happens to the ViewController when the user navigates to the different page in the middle of async loading? Would it keep the invisible ViewController in the app memory until the async task finishes?
There is no strong reference cycle (retain cycle) here. If you employ a strong reference to self, it is resolved as soon as the dispatch block runs. You theoretically could use strong reference here if you needed to.
Having said that, I would advise using a weak reference in this case. There's no point in maintaining a strong reference for the duration of the time consuming process solely for the purpose of updating a text field for a view that has already been dismissed. If you were updating other model objects or the like, perhaps you might need to keep the strong reference, but you don't need to do so in this case. As a general principle, one should release memory as soon as reasonably possible.
Even better, I'd also look at the "long running async operation" and decide whether I really want it to continue to run after the view controller has been dismissed. If not, I'd be inclined to also make the request cancelable and then have deinit cancel the request. And, in that case, you would definitely want to use weak reference (or else deinit wouldn't be called until the long running async operation finishes).

Closures vs Delegate pattern

I'm working with network request classes and I'm concerned about crashes. For instance, working with closures is really easy as you pass a callback method to a function:
// some network client
func executeHttpRequest(#callback: (success: Bool) -> Void) {
// http request
callback(true)
}
// View Controller
func reload() {
networkClient.executeHttpRequest() { (success) -> Void in
self.myLabel.text = "it succeeded" // NOTE THIS CALL
}
}
However, since the process that should execute the callback is async, when callbacks interact with container class element (in this case an UIKit class) it may be vulnerable to crashes in situations like
The user navigated to another View Controller while the async task was still executing
The user pressed the home button while the async task was still executing
Etc...
So, when the callback finally gets fired, self.myLabel.text might result in a crash, as the View Controller to whom self was refering could already be deallocated.
Up to this point. Am I right or do swift implement something internally so that this never happens?
If I am right, then here's when the delegate pattern comes in handy, as delegate variables are weak references, which means, they are not kept in memory if deallocated.
// some network client
// NOTE this variable is an OPTIONAL and it's also a WEAK REFERENCE
weak var delegate: NetworkClientDelegate?
func executeHttpRequest() {
// http request
if let delegate = self.delegate {
delegate.callback(success: true)
}
}
Note how self.delegate, since it is a weak reference, it will point to nil if the View Controller (who implements the NetworkClientDelegate protocol) gets deallocated, and the callback is not called in that case.
My question would be: do closures have anything special that makes them a good choice in scenarios similar to this one, rather than going back to delegate pattern? It would be good if examples of closures (that won't end up in crashes due to nil pointer) are provided. Thanks.
So, when the callback finally gets fired, self.myLabel.text might result in a crash, as the View Controller to whom self was referring could already be deallocated.
If self has been imported into the closure as a strong reference, it is guaranteed that self will not be deallocated up until the closure has been finished executing. That is, the view controller is still alive when the closure gets called - even if it's view is not visible at this time. Statement self.myLabel.text = "it succeeded" will be executed, but even the label will not be visible, it will not crash.
There is, though, a subtle issue which can lead to a crash under certain circumstances:
Suppose, the closure has the last and only strong reference to the view controller. The closure finishes, and subsequently gets deallocated, which also releases the last strong reference to the view controller. This inevitable will call the dealloc method of the view controller. The dealloc method will execute on the same thread where the closure will be executed. Now, that the view controller is a UIKit object, it MUST be guaranteed that all methods send to this object will be executed on the main thread. So, IFF dealloc will be actually executed on some other thread, your code may crash.
A suitable approach would require to "cancel" an asynchronous task whose result is no longer needed by the view controller when it is "closed". This, of course, requires that your "task" can be cancelled.
To alleviate some issues with your former approach, you might capture a weak reference of your view controller instead of a strong reference when defining the closure. This would not prevent the asynchronous task to run up to its completion, but in the completion handler you can check whether the view controller is still alive, and just bail out if it does not exists anymore.
And, if you need to "keep" an UIKit object in some closure which may execute on some arbitrary thread, take care that this might be the last strong reference, and ensure this last strong reference gets released on the main thread.
See also: Using weak self in dispatch_async function
Edit:
My question would be: do closures have anything special that makes them a good choice in scenarios similar to this one, rather than going back to delegate pattern?
I would say, closures are the "better" approach in many use-cases:
Delegates are more prone to issues like circular references than closures (since they are "owned" by an object, and this object might be captured as a variable in the delegate).
The classic use-case for closure as completion handlers also improves the "locality" of your code making it more comprehensible: you state what shall happen when a task finished right after the statement invoking the task - no matter how long that may take.
The huge advantage with closures versus regular "functions" is that a closure captures the whole "context" at the time when it is defined. That is, it can refer to variables and "import" them into the closure at the time when it is defined - and use it when it executes, no matter when this happens, and when the original "stack" at definition-time is gone already.
If I were you I would use closures since they are more convenient and flexible than delegation in this scenario.
Regarding the user navigating to other view controllers and the async operation still executing in the background you could keep a reference to those operations and whenever the user navigates away from the view controller you could cancel them.
Alternatively, you could verify if the view controller's view is visible before updating the UI:
viewController.isViewLoaded && viewController.view.window
Regarding the app entering background/foreground, you could pause/resume the operations by overridng the applicationDidEnterBackground and applicationWillEnterForeground or registering for the appropriate notifications: UIApplicationWillEnterForegroundNotification / UIApplicationDidEnterBackgroundNotification
I highly recommend you to use AFNetworking since it's API offers all the things I mentioned above, and much more.

Resources