如有错误,欢迎斧正。 代码环境:Xcode9 swift4.
-
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 = "" } } }
-
多重嵌套。如果在闭包中继续包含一个闭包,是否需要给每一个嵌套加上[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 } } } }
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.
-
DispatchQueue.global().async
- DispatchQueue.main-->block-->self
闭包被
DispatchQueue.main
持有,但是执行后就移除了引用,而self及属性不持有block
或DispatchQueue.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" } } } }
-
同DispatchQueue.main还有几个类似的强引用。都是block强引用self,但self不需要强引用block
-
UIView
的animate(withDuration duration: TimeInterval, animations: @escaping () -> Swift.Void)
-
OperationQueue.main
的addOperation(_ block: @escaping () -> Swift.Void)
-
Alamofire
的request
类方法
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" } } }
-