# 常见的[weak self]案例清单(持续更新)

2017-12-08  本文已影响51人  tdt

如有错误,欢迎斧正。 代码环境:Xcode9 swift4.

  1. addObserver(forName:object:queue:using:)必须使用[weak self]

    stackover上相关问题:question1 , question2

    官方文档显示如下:

    The block is copied by the notification center and (the copy) held until the observer registration is removed.

    • NotificationCenter-->block-->self

    闭包会被NotificationCenter持有,并且执行之后也并不会自动移除。所以self不会被自动释放。

    解决办法:self使用弱引用

    NotificationCenter.default.addObserver(forName: NSNotification.Name.RetainCycle, object: nil, queue: nil) {[weak self] (notification) in
        guard let `self` = self else { return }
    }
    

    PS: 见到有人说和object是否传入self有关。自己测试了一下,无论是否在object传入self,都不影响结果。测试相关代码如下,ViewController1和ViewController2均未释放:

    import UIKit
    class ViewController1: UIViewController {
    
        deinit {
            //for iOS9-
            NotificationCenter.default.removeObserver(self)
            print("ViewController deinit")
        }
    
        override func viewDidLoad() {
            super.viewDidLoad()
            self.title = "object obtains self"
            NotificationCenter.default.addObserver(forName: NSNotification.Name.RetainCycle, object: self, queue: OperationQueue.main) { (_) in
                self.title = ""
            }
        }
    }
    
    class ViewController2: UIViewController {
        
        deinit {
            //for iOS9-
           NotificationCenter.default.removeObserver(self)
           print("ViewController deinit")
       }
       
       override func viewDidLoad() {
        super.viewDidLoad()
        self.title = "object obtains self"
        NotificationCenter.default.addObserver(forName: NSNotification.Name.RetainCycle, object: nil, queue: OperationQueue.main) { (_) in
            self.title = ""
            }
        }
    }
    
  2. 多重嵌套。如果在闭包中继续包含一个闭包,是否需要给每一个嵌套加上[weak self]呢?

    答案是否定的。只需要使用一次即可。如下面的例子。

    • self-->foo-->barclosure
    • barclosure-->(weak)self
    import UIKit
    
    class Foo {
        
        func bar(closure:()->()) {
            print(vc)
        }
        
        var vc:UIViewController!
        
    }
    
    class ViewController3: UIViewController {
    
        deinit {
            //for iOS9-
            NotificationCenter.default.removeObserver(self)
            print("ViewController deinit")
        }
        
        var foo = Foo()
        
        override func viewDidLoad() {
            super.viewDidLoad()
            self.title = "object does not obtain self"
            //self.foo.vc = self 可不能写在这里,真循环引用了
            NotificationCenter.default.addObserver(forName: NSNotification.Name.RetainCycle, object: nil, queue: OperationQueue.main) { [weak self](_) in
                //定义一次即可,嵌套的就不再需要继续定义了。
                guard let `self` = self else {return}
                
                self.title = ""
                self.foo.bar {
                    self.foo.vc = self
                }
                
            }
        }
    }
    

    引用stackoverflow上的回答

    Weak references are implemented as optionals, which are value types. Therefore you cannot directly have a strong reference to one – instead you first have to unwrap it, and then take a strong reference to the underlying instance. In this case you're simply dealing with a strong reference (exactly like my example above with strongSelf).

    However, if a weak reference is boxed (this happens with closure capture – the value type will be put into a heap-allocated box) – then you can indeed have a strong reference to that box. The effect of this is equivalent to a weak reference to the original instance, you just have an invisible bit of extra indirection.

    In fact, this is exactly what happens in the example where the outer closure weakly captures self and the inner closure 'strongly captures' that weak reference. The effect is that neither closure retains self.

  3. DispatchQueue.global().async

    • DispatchQueue.main-->block-->self

    闭包被DispatchQueue.main持有,但是执行后就移除了引用,而self及属性不持有blockDispatchQueue.main,不构成循环。因此不需要使用[weak self]

    demo代码如下。block执行前,退出ViewController,ViewController不销毁。但一旦block全部执行完后,ViewController销毁。

    import UIKit
    
    class ViewController4: UIViewController {
        
        deinit {
            print("ViewController deinit")
        }
        
        override func viewDidLoad() {
            super.viewDidLoad()
    
             let deadline = DispatchTime.now() + 5
    
            DispatchQueue.global().async {
                // Do task in default queue
                
                DispatchQueue.main.async {
                    // Do task in main queue
                    self.title = ""
                }
                
                DispatchQueue.global().asyncAfter(deadline: deadline) {
                    // Do task in main queue
                    self.title = "123"
                }
            }
        }
    }
    
  4. 同DispatchQueue.main还有几个类似的强引用。都是block强引用self,但self不需要强引用block

    • UIViewanimate(withDuration duration: TimeInterval, animations: @escaping () -> Swift.Void)
    • OperationQueue.mainaddOperation(_ block: @escaping () -> Swift.Void)
    • Alamofirerequest类方法

    demo如下

    import UIKit
    
    class ViewController5: UIViewController {
        
        deinit {
            print("ViewController deinit")
        }
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            UIView.animate(withDuration: 1) {
                self.title = "123"
            }
    
            OperationQueue.main.addOperation {
                self.title = "456"
            }
            
        }
    
    }
    
上一篇下一篇

猜你喜欢

热点阅读