Shall we always use [unowned self] inside closure in Swift

前端 未结 9 1930
耶瑟儿~
耶瑟儿~ 2020-11-22 05:22

In WWDC 2014 session 403 Intermediate Swift and transcript, there was the following slide

\"enter

相关标签:
9条回答
  • 2020-11-22 05:39

    I thought I would add some concrete examples specifically for a view controller. Many of the explanations, not just here on Stack Overflow, are really good, but I work better with real world examples (@drewag had a good start on this):

    • If you have a closure to handle a response from a network requests use weak, because they are long lived. The view controller could close before the request completes so self no longer points to a valid object when the closure is called.
    • If you have closure that handles an event on a button. This can be unowned because as soon as the view controller goes away, the button and any other items it may be referencing from self goes away at the same time. The closure block will also go away at the same time.

      class MyViewController: UIViewController {
            @IBOutlet weak var myButton: UIButton!
            let networkManager = NetworkManager()
            let buttonPressClosure: () -> Void // closure must be held in this class. 
      
            override func viewDidLoad() {
                // use unowned here
                buttonPressClosure = { [unowned self] in
                    self.changeDisplayViewMode() // won't happen after vc closes. 
                }
                // use weak here
                networkManager.fetch(query: query) { [weak self] (results, error) in
                    self?.updateUI() // could be called any time after vc closes
                }
            }
            @IBAction func buttonPress(self: Any) {
               buttonPressClosure()
            }
      
            // rest of class below.
       }
      
    0 讨论(0)
  • 2020-11-22 05:42

    According to Apple-doc

    • Weak references are always of an optional type, and automatically become nil when the instance they reference is deallocated.

    • If the captured reference will never become nil, it should always be captured as an unowned reference, rather than a weak reference

    Example -

        // if my response can nil use  [weak self]
          resource.request().onComplete { [weak self] response in
          guard let strongSelf = self else {
            return
          }
          let model = strongSelf.updateModel(response)
          strongSelf.updateUI(model)
         }
    
        // Only use [unowned self] unowned if guarantees that response never nil  
          resource.request().onComplete { [unowned self] response in
          let model = self.updateModel(response)
          self.updateUI(model)
         }
    
    0 讨论(0)
  • 2020-11-22 05:43

    Update 11/2016

    I wrote an article on this extending this answer (looking into SIL to understand what ARC does), check it out here.

    Original answer

    The previous answers don't really give straightforward rules on when to use one over the other and why, so let me add a few things.

    The unowned or weak discussion boils down to a question of lifetime of the variable and the closure that references it.

    Scenarios

    You can have two possible scenarios:

    1. The closure have the same lifetime of the variable, so the closure will be reachable only until the variable is reachable. The variable and the closure have the same lifetime. In this case you should declare the reference as unowned. A common example is the [unowned self] used in many example of small closures that do something in the context of their parent and that not being referenced anywhere else do not outlive their parents.

    2. The closure lifetime is independent from the one of the variable, the closure could still be referenced when the variable is not reachable anymore. In this case you should declare the reference as weak and verify it's not nil before using it (don't force unwrap). A common example of this is the [weak delegate] you can see in some examples of closure referencing a completely unrelated (lifetime-wise) delegate object.

    Actual Usage

    So, which will/should you actually use most of the times?

    Quoting Joe Groff from twitter:

    Unowned is faster and allows for immutability and nonoptionality.

    If you don't need weak, don't use it.

    You'll find more about unowned* inner workings here.

    * Usually also referred to as unowned(safe) to indicate that runtime checks (that lead to a crash for invalid references) are performed before accessing the unowned reference.

    0 讨论(0)
  • 2020-11-22 05:51

    There are some great answers here. But recent changes to how Swift implements weak references should change everyone's weak self vs. unowned self usage decisions. Previously, if you needed the best performance using unowned self was superior to weak self, as long as you could be certain that self would never be nil, because accessing unowned self is much faster than accessing weak self.

    But Mike Ash has documented how Swift has updated the implementation of weak vars to use side-tables and how this substantially improves weak self performance.

    https://mikeash.com/pyblog/friday-qa-2017-09-22-swift-4-weak-references.html

    Now that there isn't a significant performance penalty to weak self, I believe we should default to using it going forward. The benefit of weak self is that it's an optional, which makes it far easier to write more correct code, it's basically the reason Swift is such a great language. You may think you know which situations are safe for the use of unowned self, but my experience reviewing lots of other developers code is, most don't. I've fixed lots of crashes where unowned self was deallocated, usually in situations where a background thread completes after a controller is deallocated.

    Bugs and crashes are the most time-consuming, painful and expensive parts of programming. Do your best to write correct code and avoid them. I recommend making it a rule to never force unwrap optionals and never use unowned self instead of weak self. You won't lose anything missing the times force unwrapping and unowned self actually are safe. But you'll gain a lot from eliminating hard to find and debug crashes and bugs.

    0 讨论(0)
  • 2020-11-22 05:52
    import UIKit
    
    class ViewController: UIViewController {
    
        override func viewDidLoad() {
            super.viewDidLoad()
            // Do any additional setup after loading the view.
    
            let storyboard = UIStoryboard(name: "Main", bundle: nil)
            let controller = storyboard.instantiateViewController(withIdentifier: "AnotherViewController")
            self.navigationController?.pushViewController(controller, animated: true)
    
        }
    
    }
    
    
    
    import UIKit
    class AnotherViewController: UIViewController {
    
        var name : String!
    
        deinit {
            print("Deint AnotherViewController")
        }
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            print(CFGetRetainCount(self))
    
            /*
                When you test please comment out or vice versa
    
             */
    
    //        // Should not use unowned here. Because unowned is used where not deallocated. or gurranted object alive. If you immediate click back button app will crash here. Though there will no retain cycles
    //        clouser(string: "") { [unowned self] (boolValue)  in
    //            self.name = "some"
    //        }
    //
    
    
    //
    //        // There will be a retain cycle. because viewcontroller has a strong refference to this clouser and as well as clouser (self.name) has a strong refferennce to the viewcontroller. Deint AnotherViewController will not print
    //        clouser(string: "") { (boolValue)  in
    //            self.name = "some"
    //        }
    //
    //
    
    
    //        // no retain cycle here. because viewcontroller has a strong refference to this clouser. But clouser (self.name) has a weak refferennce to the viewcontroller. Deint AnotherViewController will  print. As we forcefully made viewcontroller weak so its now optional type. migh be nil. and we added a ? (self?)
    //
    //        clouser(string: "") { [weak self] (boolValue)  in
    //            self?.name = "some"
    //        }
    
    
            // no retain cycle here. because viewcontroller has a strong refference to this clouser. But clouser nos refference to the viewcontroller. Deint AnotherViewController will  print. As we forcefully made viewcontroller weak so its now optional type. migh be nil. and we added a ? (self?)
    
            clouser(string: "") {  (boolValue)  in
                print("some")
                print(CFGetRetainCount(self))
    
            }
    
        }
    
    
        func clouser(string: String, completion: @escaping (Bool) -> ()) {
            // some heavy task
            DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
                completion(true)
            }
    
        }
    
    }
    

    If you do not sure about [unowned self] then use [weak self]

    0 讨论(0)
  • 2020-11-22 05:58

    If none of the above makes sense:

    tl;dr

    Just like an implicitly unwrapped optional, If you can guarantee that the reference will not be nil at its point of use, use unowned. If not, then you should be using weak.

    Explanation:

    I retrieved the following below at: weak unowned link. From what I gathered, unowned self can't be nil but weak self can be, and unowned self can lead to dangling pointers...something infamous in Objective-C. Hope it helps

    "UNOWNED Weak and unowned references behave similarly but are NOT the same."

    Unowned references, like weak references, do not increase the retain count of the object being referred. However, in Swift, an unowned reference has the added benefit of not being an Optional. This makes them easier to manage rather than resorting to using optional binding. This is not unlike Implicitly Unwrapped Optionals . In addition, unowned references are non-zeroing. This means that when the object is deallocated, it does not zero out the pointer. This means that use of unowned references can, in some cases, lead to dangling pointers. For you nerds out there that remember the Objective-C days like I do, unowned references map to unsafe_unretained references.

    This is where it gets a little confusing.

    Weak and unowned references both do not increase retain counts.

    They can both be used to break retain cycles. So when do we use them?!

    According to Apple's docs:

    “Use a weak reference whenever it is valid for that reference to become nil at some point during its lifetime. Conversely, use an unowned reference when you know that the reference will never be nil once it has been set during initialisation.”

    0 讨论(0)
提交回复
热议问题