1

During the following 0.5 second delay, the tableViewManager might be deallocated. I want to ensure that the textView is weak when interacting with it inside the nested closure. How would I go about this?

tableViewManager.textViewDidBeginEditing = { [weak self] textView, indexPath in
    asyncAfter(seconds: 0.5) {
        let value = textView.tag // touched here
        self?.tableView.scrollToRow(at: indexPath, at: .top, animated: true)
    }
}

Edit: Please stop supplying answers about how to make self weak. This question is not asking about 'self'. The textView. The teeeeexttt viiieeewww.

Jonesy
  • 195
  • 8
  • 2
    you should be able to add `[weak self]` after the `asyncAfter` – Scriptable Jan 22 '19 at 13:40
  • I want to touch the textView parameter. Not self. – Jonesy Jan 22 '19 at 13:51
  • 2
    `textView` won't cause a retain cycle. Passed parameters in closures don't cause retain cycles at all. – vadian Jan 22 '19 at 13:56
  • Thanks for your response @vadian. A full answer with a supporting explanation would be appreciated. – Jonesy Jan 22 '19 at 13:58
  • I update my answer. I think this could be a duplicate of [How can you capture multiple arguments weakly in a Swift closure](https://stackoverflow.com/a/28015455/10830091) – om-ha Jan 22 '19 at 14:07
  • I can't find a reference. But it has been mentioned in a WWDC video about `NSKeyValueObservation` explicitly that the observed object is passed as a parameter to avoid a retain cycle. And GCD closures don't cause retain cycles either. – vadian Jan 22 '19 at 14:10
  • @vadian I'm confused when you say 'passed as a parameter'. of course it's passed as a parameter. it's a parameter! – Jonesy Jan 22 '19 at 14:17
  • https://bugs.swift.org/browse/SR-9723 – Jonesy Jan 22 '19 at 14:27
  • You already declared `textView` as `weak`. From that point on you'll always have a weak reference to `textView`, unless you downgrade it to `unowned`. – Cristik Jan 24 '19 at 09:43

3 Answers3

1

You can use regular syntax:

tableViewManager.textViewDidBeginEditing = { [weak self] textView, indexPath in
    // textView is strong here
    asyncAfter(seconds: 0.5) { [weak textView] in
        // textView is weak here
        let value = textView?.tag // touched here
        self?.tableView.scrollToRow(at: indexPath, at: .top, animated: true)
    }
}

Edited:

Try to use container to avoid that compiler bug:

class WeakLink<T: AnyObject> {
    weak var value: T?

    init(_ value: T) {
        self.value = value
    }
}

tableViewManager.textViewDidBeginEditing = { [weak self] textView, indexPath in
    let weakTextView = WeakLink(textView)
    asyncAfter(seconds: 0.5) {
        // textView is weak here
        let textView = weakTextView.value
        let value = textView?.tag // touched here
        self?.tableView.scrollToRow(at: indexPath, at: .top, animated: true)
    }
}
J. Doe
  • 114
  • 5
0

if you put [weak self] in the top closure it will be weak for all of the nested ones... and then consider safe unwrapping it if you do not want it optional in the main closure.

tableViewManager.textViewDidBeginEditing = { [weak self] textView, indexPath in
  guard let self = self else { return } // optional...
  asyncAfter(seconds: 0.5) {
     let value = textView.tag // touched here
     self?.tableView.scrollToRow(at: indexPath, at: .top, animated: true)
  }
}

UPDATE:

I think if tableViewManager will be deallocated the closure will never be called... right? so no need to worry about "textView" being optional. it is a closure parameter... so you can set it only in closure declaration.

ARC will delete the closure automatically as soon as the tableViewManager reference is gone.

  • Again, the question is not about self or the tableView. Read the question a little closer please. – Jonesy Jan 22 '19 at 13:54
  • oh I see, I think if tableViewManager will be deallocated the closure will never be called... right? so no need to worry about "textView" being optional. it is a closure parameter... so you can set it only in closure declaration – Cosneanu Cristian Jan 22 '19 at 13:57
  • Believe is not the most productive way of proofing something, test it first, remove manually the reference to tableViewManager... you can set another asyncAfter to do that... – Cosneanu Cristian Jan 22 '19 at 14:07
0

This is not possible. This has now been recognised as a bug.

Jonesy
  • 195
  • 8