如何使用 addObserver 关闭方法在 Swift 5 中删除观察者

Dai*_*shi 5 memory-management nsnotificationcenter ios swift swift5

这是我的第一篇文章。我是日本的 iOS 工程师(这个月刚入职)。

我在 Swift 5 中的removeObserver方法有问题NotificationCenter

我使用闭包类型向 ViewController (VC) 添加了观察者addObserver。当 VC 的取消初始化调用时,我想删除这个观察者。

NotificationCenter.default.removeObserver(self)用VC的deinit方法写的。但是,这似乎对我不起作用。

有什么问题???

此外,如果我的代码有内存泄漏问题,请告诉我如何修复它。

这是我的一段代码。

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        NotificationCenter.default.addObserver(forName: UIApplication.didBecomeActiveNotification, object: nil, queue: nil) { [weak self] notification in

            guard let self = self else { return }
            self.loadWeather(notification.object)
        }
    }
    
    deinit {
        print(#function)
        print("ViewController died")

        NotificationCenter.default.removeObserver(self)
    }
}
Run Code Online (Sandbox Code Playgroud)

Hon*_*ney 12

基于闭包的 addObserver

如果您使用addObserver的基于闭包的变体,则令牌(not self) 是观察者。通知中心对self这种情况一无所知 。文档对此不是很清楚。

来自推特

这意味着这样做毫无意义: NotificationCenter.default.removeObserver(self)

文档推荐两种方法:

正常方式

像往常一样订阅:

let center = NSNotificationCenter.defaultCenter()
let mainQueue = NSOperationQueue.mainQueue()
self.localeChangeObserver = center.addObserverForName(NSCurrentLocaleDidChangeNotification, object: nil, queue: mainQueue) { (note) in
    print("The user's locale changed to: \(NSLocale.currentLocale().localeIdentifier)")
}
Run Code Online (Sandbox Code Playgroud)

在代码的某个点移除观察者。 NotificationCenter.default.removeObserver(self.localeChangeObserver)例如通过一个函数或在deinit

单订阅

第一次收到回调后立即移除观察者

let center = NSNotificationCenter.defaultCenter()
let mainQueue = NSOperationQueue.mainQueue()
var token: NSObjectProtocol?
token = center.addObserverForName("OneTimeNotification", object: nil, queue: mainQueue) { (note) in
    print("Received the notification!")
    center.removeObserver(token!)
}
Run Code Online (Sandbox Code Playgroud)

基于选择器的 addObserver

如果使用基于选择器的添加,self(没有标记)观察者。话虽如此,你应该避免这样做:

NotificationCenter.default.removeObserver(self)
Run Code Online (Sandbox Code Playgroud)

因为您的代码可能不是唯一添加涉及该对象的观察者的代码。移除观察者时,尽可能以最具体的细节移除它。例如,如果您使用名称和对象来注册观察者,请removeObserver(_:name:object:)与名称和对象一起使用。

只有removeObserver(something)deinit方法中调用是安全的,除此之外不要使用它removeObserver(_:name:object:)而是使用它。

removeObserver(self)外部调用不正确deinit,因为您将删除为该对象设置的所有观察值。在内部调用它deinit并没有错,而是毫无意义,因为该对象将立即被释放。

  • 这应该是公认的答案。在接受答案的第一种方法中,观察者没有添加到“self”中。因此,像“deinit”中那样删除是没有效果的。 (2认同)
  • 添加一件事:如果您的目标是 iOS 9.0 +,如果您添加了选择器,则无需手动删除观察者。iOS 会为你做到这一点。 (2认同)

Sat*_*esh 2

将观察者对象设置为当前视图控制器。

来自苹果文档,对象是

观察者想要接收其通知的对象;也就是说,只有该发送者发送的通知才会传递给观察者。

NotificationCenter.default.addObserver(forName: UIApplication.didBecomeActiveNotification,
                                       object: self,
                                       queue: nil) { [weak self] notification in
    guard let self = self else { return }
    self.loadWeather(notification.object)
}
Run Code Online (Sandbox Code Playgroud)

从中删除观察者NotificationCenter

deinit {
    NotificationCenter.default.removeObserver(self)
}
Run Code Online (Sandbox Code Playgroud)

其他方式

您还可以复制通知观察者对象并将其从NotificationCenter中删除deinit

let notificationCenter = NotificationCenter.default
var loadWeatherObserver: NSObjectProtocol?

override func viewDidLoad() {
    super.viewDidLoad()
    loadWeatherObserver = notificationCenter.addObserver(forName: UIApplication.didBecomeActiveNotification,
                                                         object: nil,
                                                         queue: nil) { [weak self] notification in
        guard let self = self else { return }
        self.loadWeather(notification.object)
    }
}

deinit {
    if (loadWeatherObserver != nil) {
        notificationCenter.removeObserver(loadWeatherObserver!)
    }
}
Run Code Online (Sandbox Code Playgroud)