闭包中的弱自我和后果示例

Weak self in closures and consequences example

我对 Whosebug 和 apple 关于 ARC 和 Weak/Unowned self (Shall we always use [unowned self] inside closure in Swift) 的文档做了一些研究。我了解了关于强引用循环的基本概念,以及它是如何不好的,因为它们会导致内存泄漏。但是,我想弄明白什么时候在闭包中使用 Weak/Unowned self。而不是进入 "theory",我认为如果有人可以根据我所拥有的最后三个案例友好地解释它们,那将真的很有帮助。我的问题是

  1. 是否可以将 weak self 放入其中(我认为对于第二种情况没有必要,因为我在某处看到 UIView 与 self 没有关联?但是,如果我把身体虚弱,请问有什么让我头疼的吗?

  2. 如果答案是否定的,你不能在所有三种情况下都把 weak self 放在任何地方,如果我这样做会发生什么(示例响应将不胜感激...例如,程序将当 VC ....

  3. 时崩溃
  4. 这就是我打算如何使用 weakSelf 在闭包外,我把 weak var weakSelf = self 然后用 weakSelf 替换闭包中的所有 self? 这样可以吗?

    Case 1:
    FIRAuth.auth()?.signInWithCredential(credential, completion: {    (user: FIRUser?, error: NSError?) in
        self.activityIndicatorEnd()
        self.performSegueWithIdentifier(SEGUE_DISCOVER_VC, sender: self)
    })
    
    Case 2:
    UIView.addKeyframeWithRelativeStartTime(0.0, relativeDuration: 0.1, animations: {
        self.messageLbl.alpha = 0.5
    })
    
    Case 3: 
    //checkUserLoggedIn sends a request to firebase and waits for a response to see if the user is still authorised
    checkUserLoggedIn { (success) in
        if success == false {
            // We should go back to login VC automatically
    
        } else {        
            self.discoverTableView.delegate = self
            self.discoverTableView.dataSource = self
    
            // Create dropdown menu
            let menuView = BTNavigationDropdownMenu(navigationController: self.navigationController, title: self.dropDownItems.first!, items: self.dropDownItems)
    
            menuView.didSelectItemAtIndexHandler = {[weak self] (indexPath: Int) -> () in
                if indexPath == 0 {
                    self?.mode = .Closest
                    self?.sortByDistance()
    
                } else if indexPath == 1 {
                    self?.mode = .Popular
                    self?.sortByPopularity()
    
                } else if indexPath == 2 {
                    self?.mode = .MyPosts
                    self?.loadMyPosts()
    
                } else {
                    print("Shouldnt get here saoihasiof")
                }
            }
    
        // Xib
            let nib = UINib(nibName: "TableSectionHeader", bundle: nil)
            self.xibRef = nib.instantiateWithOwner(self, options: nil)[0] as? TableSectionHeader
            self.discoverTableView.registerNib(nib, forHeaderFooterViewReuseIdentifier: "TableSectionHeader")
    
            // Set location Manager data
            self.locationManager.delegate = self
            self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
    
            // Check location service status
            if self.locationAuthStatus == CLAuthorizationStatus.AuthorizedWhenInUse {
                // Already authorised
                self.displayMessage.hidden = false
    
            } else if self.locationAuthStatus == CLAuthorizationStatus.NotDetermined {
                // Have not asked for location service before
                let storyboard = UIStoryboard(name: "Main", bundle: nil)
                let vc = storyboard.instantiateViewControllerWithIdentifier("LocationVC") as! LocationVC
                vc.locationVCDelegate = self
                self.presentViewController(vc, animated: true, completion: nil)
    
            } else {
                let alertController = UIAlertController(title: "Enable Location", message: "location is required to load nearby posts", preferredStyle: .Alert)
                let cancelAction = UIAlertAction(title: "Cancel", style: .Default, handler: nil)
                let settingsAction = UIAlertAction(title: "Settings", style: .Default, handler: { (action: UIAlertAction) in
                    let settingsUrl = NSURL(string: UIApplicationOpenSettingsURLString)
                    if let url = settingsUrl {
                        UIApplication.sharedApplication().openURL(url)
                    }
                })
                alertController.addAction(settingsAction)
                alertController.addAction(cancelAction)
                self.presentViewController(alertController, animated: true, completion: nil)
    
                self.displayMessage.hidden = false
                self.displayMessage.text = "Could not determine your location to find nearby posts. Please enable location Service from settings"
            }
    
            // Styling
            self.refreshBtn.tintColor = COLOR_NAVIGATION_BUTTONS
            self.discoverTableView.backgroundColor = COLOR_DISCOVERVC_TABLEVIEW_BACKGROUND
    
            // Allow navigation bar to hide when scrolling down
            self.hidingNavBarManager = HidingNavigationBarManager(viewController: self, scrollView: self.discoverTableView)
    
            // Allow location to start updating as soon as we have permission
            self.locationManager.startUpdatingLocation()
       }
    }
    

--更新-- 我的大部分代码看起来像案例 3,其中所有内容都包含在一个闭包中,闭包在执行任何操作之前检查是否存在互联网连接。所以我可能处处都是弱者??

--更新2--

Case 4: 
// The haveInternetConnectivity function checks to see if we can reach google within 20 seconds and return true if we can 
haveInternetConnectivity { (success) in
    if success == false {
        self.dismissViewControllerAnimated()
    } else {        
        self.label.text = "You are logged in" 
        self.performSegueWithIdentifier("GoToNextVC")
    }
}

关于案例 4 的问题。 我正确地说即使这个闭包没有 weak/unowned self,它也永远不会创建强引用(和内存泄漏),因为即使 VC 在执行完成块之前被关闭,Xcode 将在确认互联网状态后尝试 运行 完成块中的代码,并且什么都不做(不会崩溃),因为 self 不再存在。一旦代码到达闭包内的最后一行,对 self 的强引用将被销毁,因此释放 VC?

所以把 [weak Self] 放在那种情况下就意味着 xcode 会忽略这些行(而不是尝试 运行 它并且没有任何反应)这将意味着更好的做法但是我手上没有任何问题

问题不应该是 "can I use weak reference," 而是 "should I use weak reference." 您使用弱引用来避免强引用循环或防止闭包在可能已被处置后挂在某些东西上。但不要只添加弱引用,因为你 可以。

  1. 在情况 1 中,您可能确实想要使用 [weak self]。为什么?因为如果在授权进行时视图控制器被解除,你真的想保留对被解除的视图控制器的引用吗?在这种情况下可能不是。

  2. 在情况 2 中,理论上您可以在 animation 块中使用 [weak self],但为什么呢?没有理由这样做。弱引用是你用完成处理程序 and/or 闭包变量做的事情,但对于动画块它没有提供任何实用程序,所以我不会在那里做。在这里使用 weak 表明对所涉及的内存语义有误解。

  3. 在情况 3 中,您有两个不同的问题。

    • didSelectItemAtIndexHandler中,可能应该使用[unowned self],因为对象自己的闭包引用了它自己。

      这可能是一个没有实际意义的问题,因为我没有看到你实际使用它 BTNavigationDropdownMenu(也许那个初始化程序正在将自己添加到导航控制器,但如果是这样的话,那不是一个设计良好的初始化程序,恕我直言).

      但作为一般概念,当一个对象有一个处理程序闭包时只能在对象仍然存在时调用,但它本身不应该导致对象被保留,你会使用 [unowned self].

    • 在更广泛的 checkUserLoggedIn 闭包中,问题是它是否是完成处理程序。如果是这样,您可能应该使用 [weak self],因为这可能会在 self 被取消时启动并成为 运行,并且您不希望 checkUserLoggedIn 保留对被取消的视图控制器的引用。但是您不想使用 [unowned self],因为如果它在闭包运行时已被释放,那会给您留下悬空指针。

      顺便说一句,你考虑:

      weak var weakSelf = self 
      

      这有点笨拙。您可以在 checkUserLoggedIn 闭包的开头使用 [weak self] 模式。如果你有一个你想使用 weak var weakSelf = ... 的例子,你应该编辑你的问题,包括一个你想在哪里使用该模式的例子。但这不是其中之一。