这是使用通知中心以一定顺序执行任务以避免内存泄漏的正确方法吗?



我正在测试通知和完成处理程序的使用情况,以特定顺序执行多个耗时的任务。一个同事让我检查可能的内存泄漏。

我的问题是:

  • 我的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)创建的通知观察者。来自文档:

返回值:作为观察者的不透明对象。通知中心将强力保留此返回值,直到移除观察者注册。

在系统释放addObserver(forName:object:queue:using:)指定的对象之前,必须调用removeObserver(_:)removeObserver(_:name:object:)

这个removeObserveraddObserver的返回值(必须存储)上被调用。它不在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)
}

相关内容

  • 没有找到相关文章

最新更新