iOS中NotificationCenter按序执行任务的合规性及内存泄漏等疑问
问题解答
1. LongTasksClass中通知与闭包的使用是否恰当?
不恰当,存在几个关键问题:
- 内部流程用通知完全冗余:你的任务流程是类内部状态(
myStatus)驱动的,却通过NotificationCenter触发下一步操作,既增加了逻辑复杂度,也提升了调试成本。完全可以直接在任务完成的闭包里调用后续方法,不需要通过发通知间接触发。 - 闭包存在循环引用风险:
secondTimeConsumingTask的完成闭包里直接使用self但未添加[weak self],如果异步任务执行期间外部对longTaskInstance的引用被释放,self会因闭包的强引用无法销毁,造成内存泄漏。 - 参数定义不规范:
doWholeOperation的完成闭包写成@escaping (()) -> ()是冗余写法,标准无参数无返回值闭包应定义为@escaping () -> Void。
核心逻辑优化示例:
去掉通知依赖,修复闭包引用问题:
func startOperation() { // 直接调用任务方法,无需发通知 doWholeOperation { print("code after last completion") } } private func doWholeOperation(_ completion: @escaping () -> Void ) { 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.doWholeOperation(completion) } case .secondOperation: secondTimeConsumingTask { [weak self] in guard let self = self else {return} print("end second: \(Date())\n") self.myStatus = .stop // 直接递归调用,推进流程 self.doWholeOperation(completion) } case .stop: myStatus = .firstOperation print("very end") completion() return } }
2. iOS 9之后是否无需移除观察者?
分两种场景判断:
- 如果你用的是block形式的
addObserver(forName:object:queue:using:)(即你代码中的写法):系统会自动管理观察者生命周期,当LongTasksClass实例被销毁时,对应的观察者会自动移除,你代码里deinit中的removeObserver(self)是多余的。 - 如果你用的是selector形式的
addObserver(_:selector:name:object:):仍然需要在deinit中手动移除观察者,否则会存在野指针风险。
⚠️ 注意:如果block里存在对self的强引用导致对象无法销毁,观察者也不会被自动移除,但这属于内存泄漏问题,并非需要手动移除观察者的范畴。
3. 是否改为单例实现会更好?
不建议,除非你的任务流程是全局唯一、无需多个独立实例的场景,否则单例会带来更多问题:
- 单例生命周期与APP一致,若任务是一次性的,单例会持续占用内存造成浪费。
- 你的
LongTasksClass有状态变量myStatus,用单例的话,多次调用startOperation会互相干扰状态,破坏原有任务流程(比如第一个任务未完成时,第二个调用会修改myStatus导致流程混乱)。 - 单例本身会提升内存泄漏风险,因为它不会被自动销毁,一旦内部存在循环引用,泄漏会持续存在。
如果需要全局访问任务实例,建议用依赖注入的方式传递,而非直接使用单例。
内容的提问来源于stack exchange,提问作者biggreentree
相关产品推荐
相关产品推荐

