Should an asynchronous swift function retain a strong reference to the object? - ios

We implemented and extension to NSData that asynchronously persists the data to a URL.
Here is a short version of the function.
extension NSData {
func writeToURL1(url:NSURL, completion: () -> Void) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), { [weak self] in
guard let strongSelf = self else { return }
strongSelf.writeToURL(url, atomically: true)
completion()
})
}
}
Here is how we use it:
var imageData = UIImageJPEGRepresentation(image, 0.8)
imageData?.writeToURL(someURL) { in ... }
The problem of course is if imageData gets deallocated before the write operation is completed strongSelf will be nil and the completion handler will never be called.
There are two solutions to that problem.
remove [weak self] (i.e. have a strong reference to self in writeToURL1.
reference imageData in the completion block (i.e. imageData?.writeToURL(someURL) { in imageData = nil ... })
What approach is more Swift friendly and what approach should we choose?
Thank you!

You should use a strong reference if you don't want the object to go away from under your feet (and doing so wouldn't create a reference cycle); a weak reference if you don't care if the object goes away from under your feet; an unowned reference if you need the object to be around, and have a strong guarantee that the object won't go away from under your feet, but a strong reference would create a cycle.
In your case, you care that the object might go away, because that means that you won't be able to save it. As you have no guarantee that something else will keep the object alive until that task is completed, you should use a strong reference.
Strong references to self in closures are a problem when you carry the closure around because it's easy to end up with a non-obvious reference cycle, but you know for a fact that the reference will be dropped right after the closure is executed, so it's not a problem here.

It sounds like you just want to strongly capture the reference to the NSData object, so removing the [weak self] is the easiest and best approach in my opinion. Usually, weak references are used to avoid retain cycles when capturing in a closure. However, you're not actually creating a retain cycle, simply a one way retain by capturing self in the block. The block is not retained by self, it's simply passed down the call stack into dispatch_async, where it is ultimately invoked and deallocated. So there is no retain cycle to avoid by using weak self, you just have a retain occur via the closure which is desirable. That is, you want to keep the data in memory until that closure is invoked.

Related

async/await, Task and [weak self]

Okay so we all know that in traditional concurrency in Swift, if you are performing (for example) a network request inside a class, and in the completion of that request you reference a function that belongs to that class, you must pass [weak self] in, like this:
func performRequest() {
apiClient.performRequest { [weak self] result in
self?.handleResult(result)
}
}
This is to stop us strongly capturing self in the closure and causing unnecessary retention/inadvertently referencing other entities that have dropped out of memory already.
How about in async/await? I'm seeing conflicting things online so I'm just going to post two examples to the community and see what you think about both:
class AsyncClass {
func function1() async {
let result = await performNetworkRequestAsync()
self.printSomething()
}
func function2() {
Task { [weak self] in
let result = await performNetworkRequestAsync()
self?.printSomething()
}
}
func function3() {
apiClient.performRequest { [weak self] result in
self?.printSomething()
}
}
func printSomething() {
print("Something")
}
}
function3 is straightforward - old fashioned concurrency means using [weak self].
function2 I think is right, because we're still capturing things in a closure so we should use [weak self].
function1 is this just handled by Swift, or should I be doing something special here?
Bottom line, there is often little point in using [weak self] capture lists with Task objects. Use cancelation patterns instead.
A few detailed considerations:
Weak capture lists are not required.
You said:
in traditional concurrency in Swift, if you are performing (for example) a network request inside a class, and in the completion of that request you reference a function that belongs to that class, you must pass [weak self] …
This is not true. Yes, it may be prudent or advisable to use the [weak self] capture list, but it is not required. The only time you “must” use a weak reference to self is when there is a persistent strong reference cycle.
For well-written asynchronous patterns (where the called routine releases the closure as soon as it is done with it), there is no persistent strong reference cycle risk. The [weak self] is not required.
Nonetheless, weak capture lists are useful.
Using [weak self] in these traditional escaping closure patterns still has utility. Specifically, in the absence of the weak reference to self, the closure will keep a strong reference to self until the asynchronous process finishes.
A common example is when you initiate a network request to show some information in a scene. If you dismiss the scene while some asynchronous network request is in progress, there is no point in keeping the view controller in memory, waiting for a network request that merely updates the associated views that are long gone.
Needless to say, the weak reference to self is really only part of the solution. If there’s no point in retaining self to wait for the result of the asynchronous call, there is often no point in having the asynchronous call continue, either. E.g., we might marry a weak reference to self with a deinit that cancels the pending asynchronous process.
Weak capture lists are less useful in Swift concurrency.
Consider this permutation of your function2:
func function2() {
Task { [weak self] in
let result = await apiClient.performNetworkRequestAsync()
self?.printSomething()
}
}
This looks like it should not keep a strong reference to self while performNetworkRequestAsync is in progress. But the reference to a property, apiClient, will introduce a strong reference, without any warning or error message. E.g., below, I let AsyncClass fall out of scope at the red signpost, but despite the [weak self] capture list, it was not released until the asynchronous process finished:
The [weak self] capture list accomplishes very little in this case. Remember that in Swift concurrency there is a lot going on behind the scenes (e.g., code after the “suspension point” is a “continuation”, etc.). It is not the same as a simple GCD dispatch. See Swift concurrency: Behind the scenes.
If, however, you make all property references weak, too, then it will work as expected:
func function2() {
Task { [weak self] in
let result = await self?.apiClient.performNetworkRequestAsync()
self?.printSomething()
}
}
Hopefully, future compiler versions will warn us of this hidden strong reference to self.
Make tasks cancelable.
Rather than worrying about whether you should use weak reference to self, one could consider simply supporting cancelation:
var task: Task<Void, Never>?
func function2() {
task = Task {
let result = await apiClient.performNetworkRequestAsync()
printSomething()
task = nil
}
}
And then,
#IBAction func didTapDismiss(_ sender: Any) {
task?.cancel()
dismiss(animated: true)
}
Now, obviously, that assumes that your task supports cancelation. Most of the Apple async API does. (But if you have written your own withUnsafeContinuation-style implementation, then you will want to periodically check Task.isCancelled or wrap your call in a withTaskCancellationHandler or other similar mechanism to add cancelation support. But this is beyond the scope of this question.)
if you are performing (for example) a network request inside a class, and in the completion of that request you reference a function that belongs to that class, you must pass [weak self] in, like this
This isn't quite true. When you create a closure in Swift, the variables that the closure references, or "closes over", are retained by default, to ensure that those objects are valid to use when the closure is called. This includes self, when self is referenced inside of the closure.
The typical retain cycle that you want to avoid requires two things:
The closure retains self, and
self retains the closure back
The retain cycle happens if self holds on to the closure strongly, and the closure holds on to self strongly — by default ARC rules with no further intervention, neither object can be released (because something has retained it), so the memory will never be freed.
There are two ways to break this cycle:
Explicitly break a link between the closure and self when you're done calling the closure, e.g. if self.action is a closure which references self, assign nil to self.action once it's called, e.g.
self.action = { /* Strongly retaining `self`! */
self.doSomething()
// Explicitly break up the cycle.
self.action = nil
}
This isn't usually applicable because it makes self.action one-shot, and you also have a retain cycle until you call self.action(). Alternatively,
Have either object not retain the other. Typically, this is done by deciding which object is the owner of the other in a parent-child relationship, and typically, self ends up retaining the closure strongly, while the closure references self weakly via weak self, to avoid retaining it
These rules are true regardless of what self is, and what the closure does: whether network calls, animation callbacks, etc.
With your original code, you only actually have a retain cycle if apiClient is a member of self, and holds on to the closure for the duration of the network request:
func performRequest() {
apiClient.performRequest { [weak self] result in
self?.handleResult(result)
}
}
If the closure is actually dispatched elsewhere (e.g., apiClient does not retain the closure directly), then you don't actually need [weak self], because there was never a cycle to begin with!
The rules are exactly the same with Swift concurrency and Task:
The closure you pass into a Task to initialize it with retains the objects it references by default (unless you use [weak ...])
Task holds on to the closure for the duration of the task (i.e., while it's executing)
You will have a retain cycle if self holds on to the Task for the duration of the execution
In the case of function2(), the Task is spun up and dispatched asynchronously, but self does not hold on to the resulting Task object, which means that there's no need for [weak self]. If instead, function2() stored the created Task, then you would have a potential retain cycle which you'd need to break up:
class AsyncClass {
var runningTask: Task?
func function4() {
// We retain `runningTask` by default.
runningTask = Task {
// Oops, the closure retains `self`!
self.printSomething()
}
}
}
If you need to hold on to the task (e.g. so you can cancel it), you'll want to avoid having the task retain self back (Task { [weak self] ... }).

If a UITableViewController captures self in a performBatchUpdates completion handler, can that cause a retain cycle?

Say I have a UITableViewController subclass which has some function in it, eg:
class MyTableVC: UITableViewController {
func doSomething() { ... }
}
and I add a function to it that calls performBatchUpdates with a completion handler which captures self:
func updateStuff() {
tableView.performBatchUpdates(someUpdates, completion: { _ in
self.doSomething()
}
}
Is there a danger of creating a retain cycle? If so, is the view controller guaranteed to be non-nil in the callback? i.e. If there's a possibility of a retain cycle, can I use [unowned self] or is it necessary to use [weak self]
There is no major issue in your solution. self will only be retained until the completion of batch update which is fine. And I'd probably do the same not to complicate the code.
Regularly it is a bit better to still have weak or unowned just to maintain similar code style through your project.
If you decide to pick one of these, weak is the only safe option here. For example, the view controller can be removed from the screen and deallocated while the table performs the update operation (the chance is really tiny but still exists) which will cause a crash in the result.

How is a strong retain cycle possible with async or static calls?

I am trying to grasp how I can recognize when a strong retain cycle is possible and requires me to use [weak/unowned self]. I've been burned by unnecessarily using [weak/unowned self] and the self was deallocated immediately before giving me a chance to use it.
For example, below is an async network call that refers to self in the closure. Can a memory leak happen here since the network call is made without storing the call it self into a variable?
NSURLSession.sharedSession().dataTaskWithURL(NSURL(string: url)!) {
(data, response, error) in
self.data = data
)
Here's another example using the NSNotificationCenter, where a call can be made later asynchronously:
NSNotificationCenter.defaultCenter().addObserverForName(
UIApplicationSignificantTimeChangeNotification, object: nil, queue: nil) {
[unowned self] _ in
self.refresh()
}
My question is in what cases is a strong retain cycle possible? If I am making an asynchronous call or static call that references self in a closure, does that make it a candidate for [weak/unowned self]? Thanks for shedding any light on this.
A retain cycle is a situation when two objects has a strong reference to each other.
You are working with static variables NSURLSession.sharedSession() & NSNotificationCenter.defaultCenter() and as you may remember:
A singleton object provides a global point of access to the resources
of its class...You obtain the global instance from a singleton class
through a factory method. The class lazily creates its sole instance
the first time it is requested and thereafter ensures that no other
instance can be created. A singleton class also prevents callers from
copying, retaining, or releasing the instance.
https://developer.apple.com/library/ios/documentation/General/Conceptual/DevPedia-CocoaCore/Singleton.html
Your "self" instance (like the others) doesn't have a strong reference to singletons objects and its closures too, that's why you don't have to worry about retain cycle in your case.
Check this great article for more details:
https://digitalleaves.com/blog/2015/05/demystifying-retain-cycles-in-arc/
In a nutshell:
The retain cycle can happen in two cases.
Case 1:
When two instances hold a strong reference to each other. You have to solve this by marking one of them as weak.
Case 2: (Which is related to your questions)
If you assign a closure to a property of a class instance and the body of that closure captures the instance.
In your two examples, no need to use weak self at all as NSNotificationCenter nor NSURLSession are properties to your class instance. (Or in other meaning, you don't have strong references to them)
Check this example where I have to use weak self:
[self.mm_drawerController setDrawerVisualStateBlock:^(MMDrawerController *drawerController, MMDrawerSide drawerSide, CGFloat percentVisible) {
if (drawerSide == MMDrawerSideRight && percentVisible == 1.0) {
[weakself showOverlayBgWithCloseButton:YES];
}
else{
[weakself hideOverlayBg];
}
}];
I have a strong reference to mm_drawerController and I assign a closure to it right?. inside this closure I want to capture self. So the closure will have a strong reference to self !! which is a disaster. In that case you will have a retain cycle. To break this cycle, use weak self inside the closure.

Concern about memory when choosing between notification vs callback closure for network calls?

Many posts seem to advise against notifications when trying to synchronize functions, but there are also other posts which caution against closure callbacks because of the potential to inadvertently retain objects and cause memory issues.
Assume inside a custom view controller is a function, foo, that uses the Bar class to get data from the server.
class CustomViewController : UIViewController {
function foo() {
// Do other stuff
// Use Bar to get data from server
Bar.getServerData()
}
}
Option 1: Define getServerData to accept a callback. Define the callback as a closure inside CustomViewController.
Option 2: Use NSNotifications instead of a callback. Inside of getServerData, post a NSNotification when the server returns data, and ensure CustomViewController is registered for the notification.
Option 1 seems desirable for all the reasons people caution against NSNotification (e.g., compiler checks, traceability), but doesn't using a callback create a potential issue where CustomViewController is unnecessarily retained and therefore potentially creating memory issues?
If so, is the right way to mitigate the risk by using a callback, but not using a closure? In other words, define a function inside CustomViewController with a signature matching the getServerData callback, and pass the pointer to this function to getServerData?
I'm always going with Option 1 you just need to remember of using [weak self] or whatever you need to 'weakify' in order to avoid memory problems.
Real world example:
filterRepository.getFiltersForType(filterType) { [weak self] (categories) in
guard let strongSelf = self, categories = categories else { return }
strongSelf.dataSource = categories
strongSelf.filteredDataSource = strongSelf.dataSource
strongSelf.tableView?.reloadData()
}
So in this example you can see that I pass reference to self to the completion closure, but as weak reference. Then I'm checking if the object still exists - if it wasn't released already, using guard statement and unwrapping weak value.
Definition of network call with completion closure:
class func getFiltersForType(type: FilterType, callback: ([FilterCategory]?) -> ()) {
connection.getFiltersCategories(type.id).response { (json, error) in
if let data = json {
callback(data.arrayValue.map { FilterCategory(attributes: $0) } )
} else {
callback(nil)
}
}
}
I'm standing for closures in that case. To avoid unnecessary retains you just need to ensure closure has proper capture list defined.

Swift ARC and blocks

I'm trying a simple example as seen here:
https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html#//apple_ref/doc/uid/TP40014097-CH20-XID_88
And this is my code. (Ignore other possible code, this is a empty project with this code written inside an empty UIViewcontroller viewDidLoad)
dispatch_async(dispatch_get_main_queue()) {
[unowned self] in
println(self)
}
I don't understand why it crashes when I run the pro
thread #1: tid = 0x1a796, 0x00284d18 libswiftCore.dylib`_swift_release_slow + 8, queue =
'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1,
address=0x458bc681)
Did something changed on the latest beta(5) and this is not supported anymore?
Thanks
edit:
Interesting that this code works on Objc
__weak MyViewController *weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"%#", weakSelf);
});
edit2:
The explanation on this link : Shall we always use [unowned self] inside closure in Swift on the difference of weak and unowned is wrong.
It's not just that weak nils and unowned doesn't. If that's the case, this should crash as well:
dispatch_async(dispatch_get_main_queue()) {
[weak self] in
println(self)
}
but it doesn't, and it prints the pointer, so, it's not nil.
[Unowned self] makes it so the closure does not create a strong reference to self and it does not automatically set it to nil if it gets deallocated either. By the time the async method is executed, self has been deallocated. That is why you are getting the crash.
It certainly doesn't make sense to use unowned in a one time asynchronous call. It would be better to capture a strong reference to it to be sure it sticks around. There still won't be a strong reference cycle because self does not own the closure.
Side Note: This cannot be all of your code as self isn't defined anywhere in your code.
unowned and weak are two different things. In Objective-C, unowned is called unsafe unretained. You can use weak in both languages. weak means that the runtime will automatically convert the reference to nil if the object is deallocated. unowned or unsafe unretained means that it will not be set to nil for you (which is why it is called "unsafe" in Objective-C.
Unowned should only ever be used in circumstances where the object will never be deallocated. In those circumstances, use weak.
Keep in mind, that if you capture a variable as weak in Swift, the reference will be made an optional so to use it you will have to unwrap it:
dispatch_async(dispatch_get_main_queue()) {
[weak self] in
if let actualSelf == self {
// do something with actualSelf
}
// you can still print the "wrapped" self because it is fine to print optionals
// even if they are `nil`
println(self)
}
But to be clear, it would still be best to use a strong reference in this circumstance:
dispatch_async(dispatch_get_main_queue()) {
println(self)
}

Resources