如何使用 addObserver 闭包方法移除 Swift 5 中的 Observer

How to removeObserver in Swift 5 using addObserver closure method

这是我的第一个 post。 我是日本人 iOS 工程师(这个月刚成为)。

我在 Swift 5 中使用 NotificationCenterremoveObserver 方法有问题。

我通过使用闭包类型 addObserver 将观察者添加到 ViewController (VC)。 我想在 VC 的取消初始化调用时删除这个 Observer。

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

有什么问题???

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

这是我的一段代码。

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)
    }
}

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

来自apple doc.s,对象是

The object whose notifications the observer wants to receive; that is, only notifications sent by this sender are delivered to the observer.

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

正在从 NotificationCenter

中删除观察者
deinit {
    NotificationCenter.default.removeObserver(self)
}

另一种方式

您还可以复制 Notification Observer 对象并将其从 deinit 中的 NotificationCenter 中删除。

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!)
    }
}

Closure-based addObserver

If you use the closure-based variant of addObserver, the token (not self) is the observer. The notification center has no knowledge at all about self in this situation. The documentation is not super clear on this.

from Twitter

意思是这样做没有意义:NotificationCenter.default.removeObserver(self)

docs推荐两种方式:

正常方式

像往常一样订阅:

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)")
}

在代码的某个位置移除观察者。 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!)
}

Selector-based addObserver

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

NotificationCenter.default.removeObserver(self)

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

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

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