Advanced Swift: Memory Management · Conclusion | raywenderlich.com


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

In this lesson, why don’t you use unowned instead of weak? Is it possible to cause any crash?

I am not sure which lesson you are referring to but, using unowned is a much stronger assertion than weak. Unowned says I don’t own that memory, but I guarantee that it won’t go away. Weak says I don’t own that memory but don’t guarantee anything.

In a way, unowned is quite similar to an implicitly unwrapped optional (or doing a force unwrap) where weak is like using a real optional.

If you want to really run with scissors and want zero overhead, you can use “unowned(unsafe)” but I don’t recommend it because if you get it wrong you get undefined behavior instead of a deterministic trap.

Hi Ray

Thank you for this course. It was quite useful in understanding memory management in Swift. I have a suggestion though.

It would be more beneficial if in the next update you could include more videos on memory management in iOS in case of ViewControllers, animations, segues etc that is focusing more on the real world application scenarios.

@shrutisharma Thank you for your feedback - much appreciated!

Hi, thanks for the great course, I was curious about how to fix the memory leak in this case:

class Worker {
  
  var myClosure: (()->())?
  
  func create(comple: @escaping () -> ()) {
    myClosure = comple
    DispatchQueue.global().asyncAfter(deadline: .now() + 1) { [weak self] in
      self?.myClosure?()
    }
  }
  
  func aMethod() {
    print(#function)
    myClosure?()
  }
  deinit { print("Worker was deinited")  }
}

class Interactor {
  
  var str = "some string"
  var worker: Worker?
  
  func startCreatingWorker() {
    worker = Worker()
    worker?.create(comple: thisEmptyfuc)
  }

  func thisEmptyfuc() {
    print("I'm printing \(str)")
  }

  deinit { print("Interactor was deinited") }
}

var interactor: Interactor? = Interactor()
interactor?.startCreatingWorker()
interactor = nil

It depends on what you want the behavior to be. Interactor has a strong reference to worker, and Worker has a strong reference back to the Interactor via the closure. You could refer back to the closure with a non-owning one by wrapping it in a protocol/class and making that reference weak. In this case, everything will be deallocated before the closure runs, and self will evaluate to nil when the closure finally does execute.

Or you could go with a more value (struct) based approach. My thought is that Interactor should not own the worker. Workers’ lifetimes should be maintained separately.

1 Like

thanks, you’re right no need for Interactor to own the worker. Like something like this, is it the right?

func startCreatingWorker() {
    let worker = Worker()
    worker.create(comple: thisEmptyfuc)
  }

Right. It comes down to a question of behavior. If you want everything to terminate early, keep the [weak self] in the closure. If you don’t, just capture strongly (no [weak self] and self. instead of self?.) since there isn’t a cycle. Then the closure will print after a second and everything will get cleaned up by ARC.

Another approach, which was suggested by Josh H. discussed this weekend at http://aflockofswifts.com zoom meetup that help run, was to make the init of Worker capture the method. Thanks again for the question!

Something like this:

import Foundation

class Worker {
  init(_ compile: @escaping () -> ()) {
    DispatchQueue.global().asyncAfter(deadline: .now() + 1) {
      compile()
    }
  }

  deinit { print("Worker was deinited")  }
}

class Interactor {

  var str = "some string"
  var worker: Worker?

  func startCreatingWorker() {
    worker = Worker {
      self.thisEmptyfunc()
    }
  }

  func cancel() {
    // ...
  }

  func thisEmptyfunc() {
    print("I'm printing \(str)")
  }

  deinit { print("Interactor was deinited") }
}


do {
  let interactor = Interactor()
  interactor.startCreatingWorker()
}

The point here is that worker doesn’t keep around a reference but just lets DispatchQueue do the closure capture so there is no cycle.