封闭中的自我弱和后果的例子

时间:2016-08-21 03:49:48

标签: swift closures automatic-ref-counting

我已经完成了有关stackoverflow和Apple关于ARC和Weak / Unowned self(Shall we always use [unowned self] inside closure in Swift)的文档的研究。我得到了关于强引用周期的基本概念以及它们如何导致内存泄漏的不好之处。但是,我试图在封闭时使用Weak / Unowned self。而不是进入“理论”,我认为如果有人可以用我最底层的三个案例来解释它们,那真的会有所帮助。我的问题是

  1. 将弱者放在所有人身上是否可以(我认为第二种情况是没有必要的,因为我看到UIView与自我无关的某个地方?但是,如果我把弱者放在那里,怎么办?有什么东西可以让我头痛吗?

  2. 如果答案是否定的话,你不能在所有三种情况下把弱自我放在任何地方,如果我这样做会发生什么(示例响应会很受欢迎...例如,当这个VC程序会崩溃....

  3. 这就是我计划使用weakSelf的方式 在闭包之外,我把弱变弱弱自我 然后用weakSelf替换所有自我关闭? 可以吗?

    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()
       }
    }
    
  4. - Update-- 我的大多数代码看起来都像是案例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的问题。 我是否正确地说,即使这个闭包没有弱/无主自我,它也永远不会创建强引用(和内存泄漏),因为即使VC在执行完成块之前被解除,Xcode也会尝试运行代码当我们确认互联网状态并且什么也不做(没有崩溃)时,在完成块内,因为自我不再存在。一旦代码到达闭包内的最后一行,对self的强引用就会被破坏,从而解除VC的负担?

    所以在这种情况下放置[弱自我]只会意味着xcode会忽略这些行(反对尝试运行它并且没有任何反应)这意味着更好的练习,但无论如何都没有问题

1 个答案:

答案 0 :(得分:17)

问题不应该是“我可以使用弱引用”,而是“我应该使用弱引用”。您可以使用弱引用来避免强引用循环,或者在封闭处理之后保持封闭不会挂起。但是,不要只是添加弱引用,因为你可以。

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

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

  3. 在案例3中,您有两个不同的问题。

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

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

      但是作为一般概念,当一个对象有一个只能在对象仍然存在时调用的处理程序闭包,但本身不应该调用该对象时,会导致保留该对象,那么你将使用{{1} }。

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

      顺便说一下,你考虑一下:

      [unowned self]

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