With or Without [Weak Self]

with or without [weak self]

The issue is that you’ve made customNetworker, the CustomNetworker, a local variable and you lose your strong reference to it as soon as this local variable falls out of scope. When the dataRequest(input:) method uses a [weak self] reference, it’s saying that you don’t want dataRequest(input:) to keep a strong reference to this CustomNetworker instance, either. As a result, with no strong references, the CustomNetworker will be deallocated.

You have a few alternatives:

  1. If you remove the [weak self] reference in the closure inside dataRequest(input:), then it will retain the CustomNetworker until the closure runs. As long as you don’t save this failureHandler (and other closures) as stored properties, you should be fine. But don’t just throw in a [weak self] because you read somewhere that you should always use weak references. Use weak references where you need to avoid strong reference cycles, which would not appear to be the case here.

  2. The other alternative is to refactor this code, moving this network request routine, dataRequest(input:) to some more long-lived object.

    E.g., we often have a long-lived network manager object that maintains the URLSession and has all these network related methods. Then, assuming you’re keeping a strong reference to this network manager instance elsewhere, you don’t need to worry about the object being deallocated when it falls out of scope. This also saves you from having to pass in the URLSession object for every request. A caller should not be dealing with these URLSession instances, anyway.

    For example, I often use a singleton network layer (kind of equivalent to URLSession.shared pattern):

    final class NetworkManager {
    static let shared = NetworkManager()

    private var session: URLSession = .shared // or create and configure this session as you need; but one session object is all you need

    private init() { }

    @discardableResult
    func performRequest(...) -> URLSessionTask {
    ...
    let task = session.dataTask(...) { [weak self] data, response, error in
    ...
    }
    task.resume()
    return task
    }
    }

    Then you can do things like:

    NetworkManager.shared.performRequest(...) { [weak self] ... in
    ...
    }
  3. The other alternative is to just keep a strong reference to this customNetworker variable (e.g. make it a property rather than a local variable). That will prevent the CustomNetworker object from falling out of scope and becoming nil because there are no more strong references to it.


Somewhat unrelated, but it would appear that you are creating a URLSession object when you instantiate your CustomNetworker object, and subsequently never invalidate it. This will leak. You should create a single URLSession object and use it for all subsequent network requests.

If you do create multiple URLSession objects, you must invalidate each one when you’re done with it (e.g., finishTasksAndInvalidate), or else you will leak. But if you just have one session that you keep alive for all future network requests (which is preferred), then that is not necessary.

When to use [self] vs [weak self] in swift blocks?

[self] indicates that self is intentionally held with a strong reference (and so some syntax is simplified). [weak self] indicates that self is held with a weak reference.

why would I use strong capture [self] inside block as there are chances of memory leak

You would use this when you know there is no reference cycle, or when you wish there to be a temporary reference cycle. Capturing self does not by itself create a cycle. There has to be a cycle. You may know from your code that there isn't. For example, the thing holding the closure may be held by some other object (rather than self). Good composition (and decomposition of complex types into smaller types) can easily lead to this.

Alternately, you may want a temporary cycle. The most common case of this URLSessionTask. The docs are very valuable here (emphasis added):

After you create a task, you start it by calling its resume() method. The session then maintains a strong reference to the task until the request finishes or fails; you don’t need to maintain a reference to the task unless it’s useful for your app’s internal bookkeeping.

Another common example is DispatchQueue, which similarly holds onto a closure until it finishes. At that point, it releases it, killing the cycle and allowing everything to deallocate. This is useful and powerful (and common!), when used with intent. It's a source of bugs when used accidentally. So Swift requires you to state your intentions and tries to make the situation explicit.

When you build your own types that retain completion handlers, you should strongly consider this pattern, too. After calling the completion handler, set it to nil (or {_ in } for non-optionals) to release anything that completion handler might be referencing.

One frustrating effect of the current situation is that developers slap [weak self] onto closures without thought. That is the opposite of what was intended. Seeing self was supposed to cause developers to pause and think about the reference graph. I'm not certain it ever really achieved this, but as a Swift programmer you should understand that this is the intent. It's not just random syntax.

What is difference between [self] in & [weak self] in in Swift?

Writing [self] is same as leaving the closure as it is meaning it could cause retain cycle unlike [weak self] , BTW no need for any of them with DispatchQueue.main.async { as GCD queues don't hold a strong reference to self

Should I use [weak self] if I don't actually reference self in my completion block?

Capturing the variables, happens only when you use it internally, a closure will NEVER capture the variables by default (not like Java inner class which ALWAYS captures this), so, it you use a variable (including self) inside the closure, it is captured.

Also you can manually capture the variables using the [weak self], [weak your_variable_here], [unowned self], [unowned your_variable_here], [self] or [your_variable_here]

If the compiler tells you variable "self" is written to but never read from, it means that you didn't use self inside, so it is completely safe not to use [weak self], because self will NOT be captured, because it is not used.

weak self or unowned self is needed only when your closure captures self, and escapes the function it is passed to, especially if it is saved in a variable.

Refer to Closures in Apple's Official Swift Documentation

Why do I have to unwrap a weak self?

self is inside a completion handler so it might not be there anymore once the callback gets fired (it might be a network operation or something that take some take and won't return a result for several seconds if not more).

You could check if self exists before accessing it instead of unwrapping:

VerifyObject.checkPause(withID: "abcde", 
runOnPause: {[weak self] (objectID) in
guard let self = self else { return }
self.doSomething()
})

Or even shorter only doSomething if self is not nil:

VerifyObject.checkPause(withID: "abcde", 
runOnPause: {[weak self] (objectID) in
self?.doSomething()
})

Or if you're absolutely sure that self will exist:

VerifyObject.checkPause(withID: "abcde", 
runOnPause: {(objectID) in
self.doSomething()
})

Just be mindful that this last one might cause reain cicles in case where the 2 objects have a strong reference to each other and they will never get deallocated.

Is it the right way using `[weak self]` in swift closure?

Your pattern has race condition. If self was deallocated at the exact same time as your completion handler closure was executing, it could crash. As a general rule, avoid using the ! forced unwrapping operator if you can.

  1. I’d lean towards the guard “early exit” pattern (reducing nested braces, making code easier to read). The standard Swift 4.2 solution is:

    someTask { [weak self] result in
    guard let self = self else { return }

    self.xxx = yyy
    self.doLongTermWork()
    self.finish()
    }
  2. Before Swift 4.2, which implemented SE-0079, we would have to do something like:

    someTask { [weak self] result in
    guard let strongSelf = self else { return }

    strongSelf.xxx = yyy
    strongSelf.doLongTermWork()
    strongSelf.finish()
    }

    You can see why we prefer the Swift 4.2 improvement, as this strongSelf syntax is inelegant.

  3. The other obvious alternative is just:

    someTask { [weak self] result in
    self?.xxx = yyy
    self?.doLongTermWork()
    self?.finish()
    }

    Sometimes you need the “weak self - strong self dance” (the first two alternatives), but it would not appear to be the case here. This is likely sufficient.

There are other scenarios/edge cases that one might contemplate, but these are the basic approaches.

Where does the weak self go?

First of all, note that you generally don't need to worry about retain cycles with DispatchQueue.main.asyncAfter, as the closure will be executed at some point. Therefore whether or not you weakly capture self, you won't be creating a permanent retain cycle (assuming that tickle.fresh also doesn't).

Whether or not you put a [weak self] capture list on the outer asyncAfter closure depends entirely on whether you want self to be retained until the closure is called (after the time you set). If you don't need self to remain alive until the closure is called, put [weak self] in, if you do, then don't put it in.

Whether or not you put a [weak self] on the inner closure (the one passed to tickle.fresh) depends on whether you've already weakly captured self in the outer closure. If you haven't, then you can put [weak self] in order to prevent the inner closure from retaining it. If however, the outer closure has already weakly captured self, then the inner closure will already have a weak reference to self, thus adding [weak self] to the inner closure will have no effect.

So, to summarise:


DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
tickle.fresh { msg in
self.paint()
}
}

self will be retained by both the outer and inner closure.


DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [weak self] in
tickle.fresh { msg in
self?.paint()
}
}

self will not be retained by either closure.


DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [weak self] in
tickle.fresh { [weak self] msg in
self?.paint()
}
}

Same as the above, the additional [weak self] for the inner closure has no effect, as self is already weakly captured by the outer closure.


DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
tickle.fresh { [weak self] msg in
self?.paint()
}
}

self will be retained by the outer closure, but not the inner closure.


Of course, it might be that you don't want self to be retained by the outer closure, but you do want it to be retained by the inner closure. In such cases, you can declare a local variable in the outer closure in order to hold a strong reference to self, when you can then capture in the inner closure:

DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [weak self] in
guard let strongSelf = self else { return }
tickle.fresh { msg in
strongSelf.paint()
}
}

Now, self won't be kept alive by the outer closure, but once it's called, if self still exists, it will be kept alive by the inner closure until that closure has been deallocated.


In response to:

Is a strong reference to a weak reference, a weak or strong reference?

Weak references are implemented as optionals, which are value types. Therefore you cannot directly have a strong reference to one – instead you first have to unwrap it, and then take a strong reference to the underlying instance. In this case you're simply dealing with a strong reference (exactly like my example above with strongSelf).

However, if a weak reference is boxed (this happens with closure capture – the value type will be put into a heap-allocated box) – then you can indeed have a strong reference to that box. The effect of this is equivalent to a weak reference to the original instance, you just have an invisible bit of extra indirection.

In fact, this is exactly what happens in the example where the outer closure weakly captures self and the inner closure 'strongly captures' that weak reference. The effect is that neither closure retains self.

async/await, Task and [weak self]

Bottom line, there is often little point in using [weak self] capture lists with Task objects. Use cancelation patterns instead.


A few detailed considerations:

  1. 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.

  2. 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.

  3. 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:

    Sample Image

    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.

  4. 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.)

Avoiding [weak self] for simple operations?

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.



Related Topics



Leave a reply



Submit