Why do I have to unwrap a weak self?

只谈情不闲聊 提交于 2021-01-28 03:31:15

问题


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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!