You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 17:47:03