提问人:biggreentree 提问时间:4/8/2023 更新时间:4/9/2023 访问量:117
这是使用通知中心按特定顺序执行任务以避免内存泄漏的正确方法吗?
is this a correct way to use Notification Center to execute tasks in a certain order avoiding memory leaks?
问:
我正在测试通知和完成处理程序的用法,以按特定顺序执行多个耗时的任务。一位同事告诉我要检查可能的内存泄漏。
我的问题是:
- 我的 LongTasksClass:这是使用通知和关闭的正确方法吗?
- 在 iOS 9 之后不删除观察者是否正确?
- 应该更好地在其他地方推出单例吗?
在我的视图控制器中,我有:
private var longTaskInstance: LongTasksClass? = LongTasksClass()
并在按钮中:
longTaskInstance?.startOperation()
我的班级
class LongTasksClass {
enum TestingEnum: String {
case firstOperation
case secondOperation
case stop
}
//public
let testNotification = NSNotification.Name("test")
//private
private var myStatus: TestingEnum = .firstOperation
private let notificationCenter = NotificationCenter.default
init() {
notificationCenter.addObserver(forName: self.testNotification, object: nil, queue: nil) { [weak self] notification in
guard let self = self else {return}
print(notification.userInfo?["info", default: "N/D"] ?? "no userInfo")
self.doWholeOperation() {
//first calls code in comletion
//then calls this
print("code after last completion")
}
}
}
func startOperation() {
notificationCenter.post(name: self.testNotification, object: nil, userInfo: ["info" : "info start operation"])
}
//MARK: - private section -
private func doWholeOperation(_ completion: @escaping (()) -> () ) {
switch myStatus {
case .firstOperation:
print("very start: \(Date())\n")
firstTimeConsumingTask { [weak self] in
guard let self = self else {return}
print("end first: \(Date())\n")
self.myStatus = .secondOperation
self.notificationCenter.post(name: self.testNotification, object: nil, userInfo: ["info" : "info sent from first task"])
}
case .secondOperation:
secondTimeConsumingTask {
print("end second: \(Date())\n")
self.myStatus = .stop
self.notificationCenter.post(name: self.testNotification, object: nil, userInfo: ["info" : "info sent from second task"])
}
case .stop:
myStatus = .firstOperation
let myCodeForCompletion: () = print("very end")
completion(myCodeForCompletion)
return
}
}
private func firstTimeConsumingTask(_ completion: @escaping () -> ()) {
print("start first task: \(Date())")
//simulating long task
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
print("inside first task: \(Date())")
completion()
}
}
private func secondTimeConsumingTask(_ completion: @escaping () -> ()) {
print("start second task: \(Date())")
//simulating long task
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
print("inside second task: \(Date())")
completion()
}
}
//should not be required aymore
deinit {
print("‼️ called deinit")
NotificationCenter.default.removeObserver(self)
}
}
答:
必须注销由 创建的通知观察程序。从文档中:addObserver(forName:object:queue:using)
返回值:充当观察者的不透明对象。通知中心会严格保留此返回值,直到您删除观察者注册。
您必须调用或在系统解除分配任何指定的对象之前调用。
removeObserver(_:)
removeObserver(_:name:object:)
addObserver(forName:object:queue:using:)
这是在返回值(必须存储)上调用的。它没有被调用.的要点是它创建了一个不透明的对象观察者;它不会成为观察者。removeObserver
addObserver
self
addObserver(forName:object:queue:using)
self
关于 iOS 9,您正在考虑 addObserver(_:selector:name:object:),
它使第一个参数(通常)成为观察者,并且通常不需要取消注册:self
如果您的应用面向 iOS 9.0 及更高版本或 macOS 10.11 及更高版本,则无需取消注册使用此函数创建的观察者。如果您忘记或无法删除观察者,系统会在下次发布观察对象时进行清理。
与往常一样,请参阅文档。
你错了(正如你怀疑的那样)。您发出的调用不是必需的,但您缺少删除您所做的观察所需的调用。deinit
作为一般规则,基于选择器的 API 更容易正确使用。人们往往只是喜欢基于块的 API,即使它们不太方便。
在您的示例中,所需的代码为:
class LongTasksClass {
...
var observer: NSObjectProtocol
...
init() {
observer = notificationCenter.addObserver(forName: self.testNotification, object: nil, queue: nil) { [weak self] notification in
...
}
}
...
deinit {
notificationCenter.removeObserver(observer)
}
评论
self
addObserver
addObserver
addObserver
self
评论