Roi Mulia
Roi Mulia

Reputation: 5896

Function inside Function retain cycle

I was wondering how to avoid retain cycle in the following scenario:

private func setupDismissCallbacks() {

  // inner func     
  func dismiss() {
     self.videoExporter?.cancel()
     self.rootViewController.dismiss(animated: true, completion: nil)
     self.delegate?.childCoordinatorDidFinish(self)
  }

  // first clousre       
  saveModalViewController.onButtonDismiss = {  [weak self] in
     // not really using `self` here
     guard let self = self else { return }
     dismiss()
  }

  // second clousre  
  saveModalViewController.onDimmedAreaDismiss = { [weak self] in
     // not really using `self` here
     guard let self = self else { return }
     dismiss()
  }

}

I have a function setupDismissCallbacks that listens to two callbacks from a saveModalViewController self property. dismiss() is an inner function inside setupDismissCallbacks that I'm using to access self values.

But inside the closures onButtonDismiss and onDimmedAreaDismiss I don't access self to call dismiss, and I can't add [weak self] into dismiss function because it's a function.

How can I verify that the calls inside dismiss won't cause retain cycle?

Upvotes: 1

Views: 763

Answers (2)

Alexander
Alexander

Reputation: 63271

Just assign your closure to a local variable. At that point, extracting out dismiss is pointless, so just inline it:

private func setupDismissCallbacks() {     
    let dismissCallback: () -> Void = {  [weak self] in
        guard let self = self else { return }
        self.videoExporter?.cancel()
        self.rootViewController.dismiss(animated: true, completion: nil)
        self.delegate?.childCoordinatorDidFinish(self)
    }

    saveModalViewController.onButtonDismiss = dismissCallback
    saveModalViewController.onDimmedAreaDismiss = dismissCallback
}

Upvotes: 2

vacawama
vacawama

Reputation: 154603

@Alexander explained the issue in the comments:

Your inner function captures self. Your two closures capture that inner function (including the context it encloses over, which includes self). Since your two closures are strongly referenced by saveModalViewController (which I assume is strongly referenced by self), you have a retain cycle.

You can break this cycle by not having dismiss capture self. Pass the SaveCoordinator to dismiss:

private func setupDismissCallbacks() {     
  func dismiss(_ coordinator: SaveCoordinator) {
     coordinator.videoExporter?.cancel()
     coordinator(animated: true, completion: nil)
     coordinator.delegate?.childCoordinatorDidFinish(coordinator)
  }

  // first clousre       
  saveModalViewController.onButtonDismiss = {  [weak self] in
     guard let self = self else { return }
     dismiss(self)
  }

  // second clousre  
  saveModalViewController.onDimmedAreaDismiss = { [weak self] in
     guard let self = self else { return }
     dismiss(self)
  }

}

Upvotes: 1

Related Questions