How to Correctly Handle Weak Self in Swift Blocks with Arguments

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.

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.

Use [weak self] in the nested block in swift

If you don't use [weak self], then for the life time of that block, you can have a circular reference between it and the object self refers to, when an object loses a reference to it, its reference drops by one, when it reaches zero then it is deallocates and reduces the reference count of any object it has a reference to. If you have a circular reference then neither are going to reach zero, because neither is going to get to zero to deallocate itself and reduce the reference to the other. For regular objects this is a problem because they will never be deallocated, for block though, it can depend on how they are used, if they are passed to a function that uses them straight away, then once they are executed they will be deallocated, and any circular references will be cut, it may be even beneficial that whilst your block is executing that it and self can't go away, but if the block is retained as an instance variable to be called, then you have a circular reference that will never go away. The way to deal with is is to use [weak self], saying references to self are weak in this block, you can then just deal with that on each time you use it, self?.myFunction() for example, or you can create a strong reference at the beginning, it used to be you used to have to use a different variable to self, but now you can go

guard let self = self else { return }

one of the good things of doing it this way, you are saying if it gets this far I want the block to execute completely, you have created a circular reference for the execution of the block and it will not go away until the block finishes executing. For example if you have a few functions starting with self?, then mid way through your block executing self could become nil, and only the first few functions are executed and the later are not.

Weird weak self and retain cycle behaviour

First of all, all printers are creating and retaining their own Delayer. The delayer takes a closure and, in turn, retains that closure.

Let's try to walk through them one by one.

Printer1

As you stated yourself, it's pretty clear why it's creating a retain cycle. You are passing the self.action instance method as the closure to the Delayer, and since all closures are reference types, passing self.action will retain its surrounding scope (which is Printer1).

Printer2

Again, pretty obvious here. You're explicitly capturing a weak reference to self inside the closure you're passing to Delayer, hence not creating a retain cycle.

Printer3

Here, a retain cycle is not created, because the self.weakAction property is called immediately, and its result (a closure which holds a weak reference to self) is passed on to Delayer. This, in effect, is the exact same thing as what's happening in Printer2.

Printer4

First, you're capturing a weak reference to self, and then fetching welf?.action and passing the result into Delayer. Again, welf?.action is called immediately, and the result (a pointer to an instance method) is passed on to Delayer. The weak reference to self is only kept for the duration of the surrounding scope (the lazy var creation scope), and passing the action instance method will retain self. This is identical to Printer1.

Printer5

Here, you're first creating a weak reference to self, and then you're capturing that weak reference inside a new closure that is passed to Delayer. Since self is never directly referenced in the passed closure, it will not capture self in that scope, only the welf weak reference. This is pretty much identical to Printer2, but with a slightly different syntax.

Personally, I would opt for the Printer2 way (creating a new closure, retaining a weak reference to self and using that to call self?.action). It makes for the easiest code to follow (as opposed to retaining a variable with a closure that weakly captures self). But, depending on what you're actual use case is, it might of course make sense.

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

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.



Related Topics



Leave a reply



Submit