Advanced Swift: Memory Management | raywenderlich.com

Memory management works like magic most of the time - until it doesn't. Find out what you need to watch out for in this course.


This is a companion discussion topic for the original entry at https://www.raywenderlich.com/1940727-advanced-swift-memory-management

Hi,

In the “part 7 - breaking Cycles” dealing with the example of the “Galileo” supernova, I understood that ‘unowned’ isn’t the solution because the variable was already deallocated before accessing it in the closure.

I took the provided final sample and after commenting the whole “Kepler” example, there was no error anymore: ‘init Galileo’ and ‘deinit Galileo’ are indicated in the debug area.
I don’t understand why this happens because we are in the same configuration of a deallocated variable before accessing.

Would you mind explaining why this occurs, please ?

Thanks for the question. Let me see if I understand. I believe you seeing:

🐣 **init Galileo**
☠️ **deinit Galileo**
**Nada**

From the code:

do {
  let galileo = Supernova("Galileo")
  DispatchQueue.global().asyncAfter(deadline: .now() + 0.1 ) { [weak galileo] in
    guard let galileo = galileo else {
      print("Nada")
      return
    }
    galileo.explode()
    print(galileo.name)
  }
}

What happens is this:

  1. galileo is allocated and initialized
  2. access to Galileo is scheduled for 100ms later but weakly retained
  3. the do scope exits
  4. Since nothing is holding onto Galileo it is deallocated
  5. The scheduled closure runs
  6. When it tries to bind the local variable, it has been released and returns nil so the guard fails and Nada is printed.

Does that make sense?

Your answer definitely makes sense but my question wasn’t clear enough, I should have been more accurate, sorry.

Uncommenting the ‘unowned’ part and commenting the ‘weak’ one, I get the error:

🐣 init   Tycho
BOOMED Tycho
☠️ deinit BOOMED Tycho
🐣 init   Kepler
🐣 init   Galileo
☠️ deinit Galileo
BOOMED Kepler
Fatal error: Attempted to read an unowned reference but object 0x6000033f3a80 was already deallocated
☠️ deinit BOOMED Kepler

I understood this error.
Now, I comment the ‘Tycho’ and the ‘Kepler’ parts while adding a ‘start’ and ‘end’ as follows:

do {
  let galileo = Supernova("Galileo")
  DispatchQueue.global().asyncAfter(deadline: .now() + 0.1 ) { [unowned galileo] in
    print("Start")
    galileo.explode()
    print(galileo.name)
    print("End")
  }
}

I thought I should get the same result but, instead, I got:

🐣 init   Galileo
☠️ deinit Galileo
Start

No error anymore (weird) and the end of the queue isn’t reached (normal).
I don’t know why there’s no error while the Galileo variable is clearly deallocated and isn’t checked as nil contrary to the ‘weak’ case.
Any ideas ?

Ah, I see. This looks like it a problem with the playground.

What I think is happening is that the playground is shutting down and swallowing the trap. You would see the proper behavior if you built in a project instead of a playground I believe. (I was hoping that the line PlaygroundPage.current.needsIndefiniteExecution = true would have fixed this but apparently does not.)

Another way you can see a trap is to just add this to the end of the playground.

Thread.sleep(forTimeInterval: 1)

You will see a crash if you using [unowned galileo] and it will go away if you use the other version.

In short, I think your understanding is correct and the strange behavior is just the playground trying to be “extra helpful.”

You’re right, the playground is the problem. :+1:
I created a blank project and everything works fine as expected.

In the end, let me emphasize the quality of this tutorial that really helped me to understand notions I used without really knowing what was under the hood.:wink:

Just a little point to finish: in the “part 4 - Challenge: Fix the Leak 1”, the provided solution in the sample code doesn’t match the video.

SAMPLE CODE

ray.friends.append(contentsOf: [Unowned(vicki),Unowned(tammy),
                              Unowned(richard),Unowned(greg),
                              Unowned(darren)])
vicki.friends.append(contentsOf: [Unowned(ray),Unowned(tammy),
                                Unowned(richard),Unowned(greg),
                                Unowned(darren)])

VIDEO
RWForum_Screenshot
It’s definitely not a problem but it’s just to have more consistency between these two explanations unless it’s been made on purpose (the second one is more elegant :ok_hand:).

Thanks for pointing that out. I created a final playground before filming and then film the playground. Most of the time they are exactly the same and it doesn’t matter which one gets copied in but in this case there was a difference. (As you say, it is sort of a peripheral difference, but still it would be better if they matched.)