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

Posted

技术标签:

【中文标题】闭包中的弱自我和后果示例【英文标题】:Weak self in closures and consequences example 【发布时间】:2016-08-21 03:49:47 【问题描述】:

我对 *** 和苹果关于 ARC 和 Weak/Unowned self (Shall we always use [unowned self] inside closure in Swift) 的文档进行了一些研究。我得到了关于强引用循环的基本概念,以及它如何不好,因为它们会导致内存泄漏。但是,我试图弄清楚何时在闭包中使用弱/无主自我。而不是进入“理论”,我认为如果有人可以根据我所拥有的最底层的三个案例来解释它们,那将真的很有帮助。我的问题是

    是否可以将弱自我放在所有这些中(我认为对于情况二没有必要,因为我在某处看到 UIView 与自我无关?但是,如果我将弱自我放在那里怎么办,有什么东西会让我头疼吗?

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

    这就是我打算如何使用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 的问题。 我是否正确地说,即使这个闭包没有弱/无主自我,它也永远不会产生强引用(和内存泄漏),因为即使在执行完成块之前关闭 VC,Xcode 也会尝试运行代码当我们确认互联网状态并且什么都不做(没有崩溃)时,在完成块内,因为 self 不再存在。一旦代码到达闭包内的最后一行,对 self 的强引用就会被破坏,从而释放 VC?

所以在这种情况下放置 [weak Self] 只会意味着 xcode 会忽略这些行(因为反对尝试运行它并且没有任何反应),这意味着更好的做法,但无论哪种方式都没有问题

【问题讨论】:

【参考方案1】:

问题不应该是“我可以使用弱引用吗”,而应该是“我应该使用弱引用吗”。你使用弱引用来避免强引用循环,或者防止闭包在可能已经被释放后挂在某个东西上。但不要只添加弱引用,因为您可以。

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

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

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

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

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

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

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

    顺便说一句,你考虑:

    weak var weakSelf = self 
    

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

【讨论】:

您能否详细说明“不提供实用程序”的含义。此外,公平地说,任何涉及等待互联网请求的任何类型的关闭总是需要一个弱或无主的自我吗?在几乎所有这些情况下,它都是弱自我,这是否正确? animation闭包中重新弱引用不提供实用程序:提供给animateWithDurationanimation闭包不是稍后运行的完成处理程序(尽管有一个单独的completion闭包,但这完全是一个单独的蜡球)。它会立即被调用,并启动动画,但闭包不会在动画期间保留对 self 的任何强引用。 重新网络关闭总是需要弱/无主:不,当然不是这样。例如,您可能会在此闭包中执行一些必须运行的操作(例如,更新本地数据库以完成网​​络请求;将下载的图像保存到缓存中;等等)。仅当您不需要/不希望闭包中的代码保持对对象的引用时才使用弱 (例如,您只是更新 UI,而不是更新缓存或数据库或模型结构)。不要盲目地使用weak:查看闭包中的内容并决定它是否应该(或必须)弱。 如果VC永远不会被解雇,我真的不需要担心强引用循环吗? 把我学到的知识,请你看看我对案例 4 的更新以及相关的问题。

以上是关于闭包中的弱自我和后果示例的主要内容,如果未能解决你的问题,请参考以下文章

Objective-C 中的弱键字典

在闭包中引用属性需要明确的“自我”。使捕获语义明确

js中的匿名函数和闭包总结篇

js中的匿名函数和闭包总结篇

深入理解闭包中的委托

Objective-C 中的弱属性和强属性设置器属性