Closures vs Delegate pattern - ios

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.

Related

Avoiding [weak self] for simple operations?

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.

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

Im confused on how can I manipulate the properties inside an instance when it gets deinitialized?

I have this from either the Apple documentation or the swift book
When an instance gets deinitialized, you still have access to the
properties inside the instance and can manipulate them as needed
before the instance totally goes away.
I'm confused, do they mean when we for example do some mathematical action using the instances property in the deinit() method? or lets say when we print a property of type string that was part of a specific instance, also from the deinit() method?
If so, then is the deinit() method the only way to manipulate a property when it is being deinitialized?
if you have a swift class with a a var you know you have to clean up after because ARC can't free it (e.g. C memory), you can still do that in deinit. The pointers stored in the properties are still valid!
it isn't useful for much more though (ok end observing with the notification center or kvo) BECAUSE there is no guarantee WHEN deist is called. ONLY that it is called before deallocation [whenever that is]
deinit is called right before deallocation (when the retainCount reaches 0), so all your properties are still valid and you can print your string. You don't need to set properties to nil explicitly in deinit as that happens automatically.
This being said, most classes don't even need deinit implemented
Most of the time I used deinit to remove observer that the instance is registered to, post any notifications if needed, and things like that.
As far as I know, the deinit method gets called just before the instance gets deinitialized, to give you a final opportuninty to do whatever you need to do (cleanup, close a file, terminate a network connection, etc).
What the documentation says is that, at the time deinit is called your object has not been deinitialized yet (but will be very soon), so you still can (for the last time) access its properties.

Swift : deinit is not calling on back button action

In objective -C on back button dealloc method gets called. Anything similar to that in swift ?
As you seem to understand, deinit is the equivalent of dealloc. If it's not being called, your object is not being destroyed, which means something has a strong reference to it. This is identical in ObjC and Swift. When you remove your last strong reference, deinit will be called.
Neither dealloc nor deinit has anything to do with a "back button action." They are only related to freeing objects, and should generally only perform resource cleanup. If you're relying on them being called in response to a user action, you probably have a design error.
From the Swift Documentation:
A deinitializer is called immediately before a class instance is deallocated. You write deinitializers with the deinit keyword, similar to how intializers are written with the init keyword. Deinitializers are only available on class types.
Typically you don’t need to perform manual clean-up when your instances are deallocated. However, when you are working with your own resources, you might need to perform some additional clean-up yourself. For example, if you create a custom class to open a file and write some data to it, you might need to close the file before the class instance is deallocated.

Weak/Strong "dance" in Manual Memory Management

Imagine the following scenario using Manual Memory Management (aka non-ARC):
I have a VC that passes a block to a class method. Before the block being executed, the VC is popped up out of a UINavigationController. A weak reference in the form of __block MyVC *weakSelf = self is passed to the block which is then converted to MyVC *strongSelf = weakSelf (aka weak/strong dance). The block is never retained by any of the intervenients.
In this scenario, what I am seeing in my code is:
The dealloc of the VC is called when it is popped out.
The block is eventually is called.
The app crashes because I am accessing garbage (the strongSelf is pointing to it).
My question is: I don't want my VC to stay alive until the block eventually executes, but once the block is executed I want to confirm that strongSelf is valid.
I have checked this (non-trivial example by Apple) which doesn't work because it's designed with ARC in mind. So how can I have the same behaviour with MMM? Ideally I want to have what __weak does: if the retainCount reaches zero, I want its references to point to nil and not to garbage.
After reading this from Apple:
In some cases you can use __unsafe_unretained if the class isn’t
__weak compatible. This can, however, become impractical for nontrivial cycles because it can be hard or impossible to validate
that the __unsafe_unretained pointer is still valid and still points
to the same object in question.
Since I don't have access to __weak what I want to do is even possible?
I battled with this exact issue back in the iOS 4.x days. It's not easy without weak pointers giving you a hand!
If you are guaranteed that block is executed on the main thread at a later point (i.e. where strongSelf is assigned from weakSelf) then you basically need a place to store a "isDead" flag, which you set when the VC is dealloced. You then check this flag before doing anything with weakSelf/strongSelf. One solution is this:
You need a class who's only job is to store this "isDead" flag in a member variable. Something like a NSMutableBoolean. I won't write one out, it's simple enough, it just needs a single BOOL property.
In your VC's -[NSObject init] method you create an instance of this flag object, which should initially be set to false.
You then pass this object through to any block you queue for later execution, such that it is automatically retained/released by the block (i.e. without going through the weak/strong dance). Inside the block, you check if the flag is still NO before doing anything with weakSelf.
The key of course is to set the flag to YES inside the VC's -[NSObject dealloc] method, and then release it. If any blocks are still pending execution, they will have already retained the flag object themselves, and when they are later executed they will query the flag discover that the VC is now dead.
This sounds cumbersome (and it is, a bit) but the idea is that the "isDead" flag lives outside the scope of the VC and is therefore not tied to it's lifetime. As for thread safety, as long as you only set/query the flag inside the VC's init/dealloc method and when the block is executed (on the main thread, not on a background thread) it will be thread safe.
I don't want my VC to stay alive until the block eventually executes
But why does it matter? The memory of one VC shouldn't be that much, and if the block performs any UI actions on the VC, that's okay too, since the VC isn't displayed anyway.
Basically, the block should not capture a weak reference to the VC if the VC doesn't retain the block. Using non-zeroing weak reference means you guarantee that the object will stay alive, which is not the case here. I would suggest you not do this weak thing.

Resources