问题
I have created a class called VerifyObject
, that contains a function with a signature like that
typealias handlerCodeID = (String) ->Void
class func checkPause(withID:String?,
runOnPause: handlerCodeID?)
When I run that, I need to pass a weak self reference to inside the closure, using
VerifyObject.checkPause(withID: "abcde",
runOnPause: {[weak self] (objectID) in
self.doSomething()
})
Xcode complains that the self
in doSomething
must be unwrapped to
self!.doSomething()
why? Does not make sense.
回答1:
self is inside a completion handler so it might not be there anymore once the callback gets fired (it might be a network operation or something that take some take and won't return a result for several seconds if not more).
You could check if self exists before accessing it instead of unwrapping:
VerifyObject.checkPause(withID: "abcde",
runOnPause: {[weak self] (objectID) in
guard let self = self else { return }
self.doSomething()
})
Or even shorter only doSomething
if self is not nil:
VerifyObject.checkPause(withID: "abcde",
runOnPause: {[weak self] (objectID) in
self?.doSomething()
})
Or if you're absolutely sure that self will exist:
VerifyObject.checkPause(withID: "abcde",
runOnPause: {(objectID) in
self.doSomething()
})
Just be mindful that this last one might cause reain cicles in case where the 2 objects have a strong reference to each other and they will never get deallocated.
回答2:
While the accepted answer explains how you handle weak self
according to different scenarios, I feel like it fails to properly explain why you gotta unwrap weak
references and why use weak
references in the first place. Understanding this chain will automatically make you understand why you have to unwrap a weak self
Closures are awesome, if you handle them properly
The usual major caveat(that beginners often tend to overlook) with closures is that they capture the class they are declared on, IF you use something that belongs to the class inside the closure.
I'll explain the 'capturing' process:
1) The closures that you declare as a property for your class are escaping
closures.
2) To explain what escaping
closures are, they don't deallocate with the block they are declared at. They instead escape and outlive the block to provide you callback.
(Also, you might have noticed that the compiler asks you to specify @escaping
exquisitely when you pass a closure as a function parameter to provide completion blocks, and this is exactly why it asks you to)
3) Hence, using something that belongs to the class(such as a property) inside an escaping
closure allows the closure to capture(retain in memory) the whole class to provide you callback, and this leads to retain cycles
Here's an example: (The same one from the link that I'll share)
Say you have this class
:
class ListViewController: UITableViewController {
private let viewModel: ListViewModel
init(viewModel: ListViewModel) {
self.viewModel = viewModel
super.init(nibName: nil, bundle: nil)
viewModel.observeNumberOfItemsChanged {// Assume this to be some closure that I have in my viewModel
// This will cause a retain cycle, since our view controller
// retains its view model, which in turn retains the view
// controller by capturing it in an escaping closure.
self.tableView.reloadData()
}
}
}
All these happen because the closure holds a strong reference to your class. To break this strong reference, you use weak self
in your callback. The weak
keyword in a nutshell deallocates the class object if it remains unused for a considerable amount of time and helps break retain cycles
And now, to your question: (You might know the answer already if you made it this far :-))
You have to unwrap weak self
because it might no longer be in memory, simple as that!
Finally, here's the link that I was talking about: Capturing objects in Swift closures
NOTE:
In certain cases, you can almost be pretty sure that the captured object will remain in memory by the time you receive callbacks, but want self
to be deallocated after that. In those cases, you can use unowned self
instead of weak self
to break free from the unwrapping hassle. This is almost the same as unwrapping an optional and hence will crash if self
is deallocated
回答3:
using [weak self] or [unowned self], creates a weak reference to avoid memory leaks u should use both of these when using clousre,
now I u just want to avoid "?" optionals use unowned reference with your self in the clousre
来源:https://stackoverflow.com/questions/57709872/why-do-i-have-to-unwrap-a-weak-self