Swift 5协议中使用@objc的报错问题及NotificationCenter观察者移除方案咨询
解决Swift @objc协议扩展中Selector的问题,以及正确管理Notification观察者
我来帮你搞定这个问题,你遇到的核心困境在于Swift协议扩展里的默认方法无法直接标记@objc,而#selector又要求目标方法能被Objective-C识别。同时你尝试的block方案因为没保存观察者token导致无法正确移除,下面给你两个可行的解决方案:
方案一:修正@objc协议的方法声明
问题的根源是trackScreenshot是协议扩展里的默认实现,并没有被声明为@objc协议的成员,所以Objective-C运行时找不到它。我们可以把这个方法加到@objc协议中,确保它能被Selector识别:
import UIKit // 让协议继承NSObjectProtocol,确保遵守的类是NSObject子类 @objc protocol TrackScreenshot: NSObjectProtocol { func registerObserver() func removeObservers() @objc func trackScreenshot() // 将方法加入协议并标记@objc } extension TrackScreenshot where Self: ScreenTracking { func registerObserver() { NotificationCenter.default.addObserver(self, selector: #selector(trackScreenshot), name: UIApplication.userDidTakeScreenshotNotification, object: nil) } func removeObservers() { NotificationCenter.default.removeObserver(self, name: UIApplication.userDidTakeScreenshotNotification, object: nil ) } // 这里的默认实现会被遵守协议的类继承 func trackScreenshot() { print(screenName.rawValue) } }
注意事项:
- 所有遵守
TrackScreenshot的类必须是NSObject的子类(因为协议继承了NSObjectProtocol) - 如果某个子类需要自定义
trackScreenshot逻辑,直接在类里重写即可,Swift会优先调用类的实现
方案二:使用Token管理Block式观察者(更推荐)
这个方案完全避开@objc的限制,利用NotificationCenter.addObserver(forName:...)返回的NSObjectProtocol token来管理观察者的生命周期,完美解决你之前无法移除观察者的问题:
import UIKit protocol TrackScreenshot: ScreenTracking { // 添加一个属性来保存观察者token var screenshotObserverToken: NSObjectProtocol? { get set } func registerObserver() func removeObservers() } extension TrackScreenshot { func registerObserver() { // 保存token,后续用于移除 screenshotObserverToken = NotificationCenter.default.addObserver(forName: UIApplication.userDidTakeScreenshotNotification, object: nil, queue: nil) { [weak self] _ in guard let self = self else { return } print(self.screenName.rawValue) } } func removeObservers() { if let token = screenshotObserverToken { NotificationCenter.default.removeObserver(token) screenshotObserverToken = nil // 清空token,避免野指针 } } }
为什么这个方案更好?
- 不需要任何@objc修饰,完全基于Swift语法,避免了Objective-C兼容性问题
- 通过token精准移除当前屏幕的观察者,不会出现旧观察者残留的情况
- 使用
[weak self]避免了循环引用,保证内存安全
你只需要让你的屏幕类遵守TrackScreenshot协议,并实现screenshotObserverToken属性(比如在类里声明var screenshotObserverToken: NSObjectProtocol?),然后在屏幕的生命周期方法(比如viewWillAppear/viewWillDisappear)里调用registerObserver()和removeObservers()就可以了。
内容的提问来源于stack exchange,提问作者Jkrist
相关产品推荐
相关产品推荐

