简体   繁体   中英

Are there potential drawbacks to using a [weak self] within a closure after declaring a strong self?

The title I used for this may not be particularly clear, so I hope that a working code sample can provide some clarity.

The issue I am facing is that I have a scenario similar to the code below:

import Foundation

class Test {
    private var isInner = false {
        didSet {
            print("isInner: \(isInner)")
        }
    }

    private func runClosure(closure: () -> ()) {
        closure()
    }

    func callClosure() {
        // Weak 1
        runClosure { [weak self] in
            self?.isInner = false
            guard let strongSelf = self else { return }
            // Can this [weak self] create problems?
            // Weak 2
            strongSelf.runClosure { [weak self] in
                self?.isInner = true
            }
        }
    }
}

let test = Test()
test.callClosure()
// The following is printed to the console
// isInner: false
// isInner: true

Everything above works precisely as intended, which is good.

My concern is the second use of [weak self] . While self is declared weak at the start of the function ( Weak1 ), I then set it to strongSelf shortly thereafter.

I could re-use my earlier strongSelf , but the functions in question are actually potentially long running operations and there exists the possibility that self could be out of scope between Weak1 and Weak2 .

However, it has been brought to my attention that the potential exists for Weak2 to be invalid, which is what I am hoping to clarify with this question.

Ultimately, all that weak does is create an optional variable for self, so I am not aware of any potential pitfalls. Additionally, Weakself1 , strongSelf , and Weakself2 are all pointing at the same memory address during the execution of callClosure() .

Let's take this step by step (line by line)

// Weak 1
runClosure { [weak self] in

The first line creates a reference to the target object, a reference which coincidentally (or not) is named self .

    self?.isInner = false

The above line makes use of the weak reference, it has no effects on the target object lifecycle.

    guard let strongSelf = self else { return }

Now, this line indeed creates a strong reference to the target object, which will extend objects's lifetime by at least the lifetime of strongSelf . Now depending on the compiler aggressiveness, strongSelf might die after passing this line (the last reference in code), or when the outer closure finishes executing.

    // Can this [weak self] create problems?
    // Weak 2
    strongSelf.runClosure { [weak self] in

Now this has almost exactly the same effect as the capture from the outer closure. It creates a reference to a possibly already deallocated instance (depending if strongSelf is still alive at this point or not).

            self?.isInner = true

This is the regular optional usage, no effects on target lifetime.

Now, assuming that runClosure runs asynchronously, there are no issues with the target object living more that expected (assuming there aren't any more strong references out there).

To summarize, an object lifetime is determined by the number of strong references exist to that object. Once all strong references are destroyed, the object will be automatically deallocated. In your particular scenario, the inner closure weakly captures and already weak reference, and this doesn't impact the lifecycle of the target object. The only player is strongSelf , which gets destroyed no later than the outer closure destroyal.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM