简体   繁体   中英

convert the reference back to a strong one inside the closure, memory management, swift

I'm experimenting the retain cycle in closure like following

 class Sample {
        deinit {
            print("Destroying Sample")
        }

        func additionOf(a: Int, b:Int) {
            print("Addition is \(a + b)")
        }

        func closure() {                
          dispatch_async(dispatch_get_global_queue(0, 0)) { [weak self] in
            self?.additionOf(3, b: 3)   
            usleep(500)                 
            self?.additionOf(3, b: 5)  
          }
        }
    }

Later at some point, I'm doing

var sample : Sample? = Sample()
sample?.closure()

dispatch_async(dispatch_get_global_queue(0, 0)) {
  usleep(100)
  sample = nil
}

The output will be

Addition is 6
Destroying Sample

This is understandable because self is nil out before doing self?.additionOf(3, b:5)

If I made a change inside the closure by creating another variable which references to [weak self] like following

dispatch_async(dispatch_get_global_queue(0, 0)) { [weak self] in
   guard let strongSelf = self else { return }
   strongSelf.additionOf(3, b: 3)
   usleep(500)
   strongSelf.additionOf(3, b: 5)
}

This time, the output is

Addition is 6
Addition is 8
Destroying C

My question is why strongSelf is not nil out after sample = nil . Is it because it is captured inside the closure before sample = nil

Let's consider your second example:

dispatch_async(dispatch_get_global_queue(0, 0)) { [weak self] in
    guard let strongSelf = self else { return }
    strongSelf.additionOf(3, b: 3)
    usleep(500)
    strongSelf.additionOf(3, b: 5)
}

This is a common pattern and it effectively says "if self has been deallocated, return immediately, otherwise establish a strong reference, strongSelf , and maintain this strong reference until the closure finishes."

So, in your example, self was not nil when this dispatched block started, so as soon as we assigned strongSelf to refer to it, we now have two strong references to this Sample object, both the original sample reference and this new strongSelf reference inside this closure.

So, when your other thread removes its own strong reference, sample , to this Sample object (either by falling out of scope or by explicitly setting sample to nil ), this strongSelf strong reference is still present and will keep the object from being deallocated (or at least not until this dispatched block finishes).

In your comment above, you ask:

I still not understand why strongSelf does not get changed since self is nil out...

Strong references never get set to nil just because some other strong reference (even if it was the original strong reference) was set to nil . That behavior of a reference getting set to nil only applies to weak references, not to strong references.

When your code performs sample = nil on the first thread, all that does is remove a strong reference. It doesn't delete the object or anything like that. It just removes its strong reference. And now that the dispatched block has its own strong reference, all that happens upon sample = nil is that the object that had two strong references now only has one strong reference left (the strongSelf reference in the dispatched block). Only when this final strong reference is removed will the object be deallocated and deinit will be called.

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