使用Swift Combine的subscribe(_ subject:)方法时出现内存泄漏问题
Combine
subscribe(_ subject:) API 内存泄漏问题解决方案 问题现象
使用Combine的subscribe<S>(_ subject: S) -> AnyCancellableAPI时,若Publisher经过eraseToAnyPublisher()包装,会导致持有AnyCancellable的对象(如示例中的SwiftViewController)无法正常释放,出现内存泄漏。示例代码如下:
class SwiftViewController: UIViewController { private var cancellable: AnyCancellable? override func viewDidLoad() { super.viewDidLoad() let subject1 = PassthroughSubject<(), Never>().eraseToAnyPublisher() let subject2 = PassthroughSubject<(), Never>() self.cancellable = subject1.subscribe(subject2) } }
复现步骤:Push进入SwiftViewController后返回,该控制器实例未被释放,存在内存泄漏。测试环境:Xcode 15.2,iOS模拟器17.4。
泄漏原因
subscribe(_ subject:)方法内部,Subject会持有传入的Publisher引用;而当Publisher是被eraseToAnyPublisher()包装后的实例时,该包装对象会反向持有Subscription(Subscription又关联到Subject),形成循环引用。由于ViewController持有AnyCancellable,AnyCancellable又持有Subscription,最终导致ViewController无法被ARC回收。
解决方案
方案一:用sink替代subscribe(_ subject:)
通过sink接收Publisher事件并手动转发到Subject,打破循环引用:
class SwiftViewController: UIViewController { private var cancellable: AnyCancellable? override func viewDidLoad() { super.viewDidLoad() let subject1 = PassthroughSubject<(), Never>().eraseToAnyPublisher() let subject2 = PassthroughSubject<(), Never>() self.cancellable = subject1 .sink(receiveValue: subject2.send) } }
方案二:避免对Publisher使用eraseToAnyPublisher()(业务允许时)
如果无需隐藏Publisher的具体类型,可直接去掉eraseToAnyPublisher(),同样能避免循环引用:
class SwiftViewController: UIViewController { private var cancellable: AnyCancellable? override func viewDidLoad() { super.viewDidLoad() let subject1 = PassthroughSubject<(), Never>() let subject2 = PassthroughSubject<(), Never>() self.cancellable = subject1.subscribe(subject2) } }
内容的提问来源于stack exchange,提问作者Yanni Wang
相关产品推荐
相关产品推荐

