提问人:Jinjiang You 提问时间:8/26/2022 更新时间:8/26/2022 访问量:128
在 Swift 闭包中调用 Objective-C 方法会导致内存泄漏
Calling Objective-C methods in Swift closure causes memory leaks
问:
我正在开发一个需要 Swift 和 Objective-C 的 IOS 应用程序。我发现在 Swift 闭包中调用 Objective-C 方法可能会导致内存泄漏 - 即使没有其他人引用,在 Objective-C 方法中访问的实例仍将保留在内存中。
假设我们有两个类和:A
B
@objc class A : NSObject {
@objc var name : String
init(name : String) {
self.name = name
print("Class A \"\(self.name)\" is constructed.")
super.init()
}
deinit {
print("Class A \"\(self.name)\" is destructed.")
}
}
@objc class B : NSObject {
@objc var nestedClass : A
init(nestedClass : A) {
self.nestedClass = nestedClass
print("Class B \"\(self.nestedClass.name)\" is constructed.")
super.init()
}
deinit {
print("Class B \"\(self. nestedClass.name)\" is destructed.")
}
}
在我们的类和方法中,我们不断实例化类在闭包中:ContentViewModel
startWorking
B
class ContentViewModel : ObservableObject {
private var _isWorking = ManagedAtomic<Bool>(false)
private(set) var isWorking: Bool {
set {
self.objectWillChange.send()
self._isWorking.store(newValue, ordering: .relaxed)
}
get {
return self._isWorking.load(ordering: .relaxed)
}
}
//To perform async work
let dispatchQueue = DispatchQueue(label: "ContentViewModel", qos: .userInteractive, attributes: .concurrent)
//Objective-C interface, used to call objc methods
let objcInterface = ObjcInterface()
//Start
func startWorking() {
self.isWorking = true
var count = 0
self.dispatchQueue.async {
while self.isWorking {
//Instantiate class A and B
let b = B(nestedClass: A(name: String(count)))
//In "process" method, we do nothing but access "b.nestedClass". We do not retain it.
self.objcInterface.process(b)
count += 1
//Now, "b" is no longer referenced. So, it should be destructed.
//Also, its nested instance "b.nestedClass" should also be destructed.
}
}
}
//Stop
func stopWorking() {
self.isWorking = false
}
}
在类的方法中,我们什么都不做,只是访问:process
ObjcInterface
b.nestedClass
@implementation ObjcInterface
- (void)process:(B* _Nonnull)b {
b.nestedClass;
}
@end
如果我们调用类的方法(例如,通过UI中的按钮),我们期望该类并保持构造和析构。但是,如果我们看一下控制台:startWorking
ContentViewModel
A
B
Class A "0" is constructed.
Class B "0" is constructed.
Class B "0" is destructed.
Class A "1" is constructed.
Class B "1" is constructed.
Class B "1" is destructed.
Class A "2" is constructed.
Class B "2" is constructed.
Class B "2" is destructed.
Class A "3" is constructed.
Class B "3" is constructed.
Class B "3" is destructed.
Class A "4" is constructed.
Class B "4" is constructed.
Class B "4" is destructed.
Class A "5" is constructed.
Class B "5" is constructed.
Class B "5" is destructed.
Class A "6" is constructed.
Class B "6" is constructed.
Class B "6" is destructed.
......
我们会发现这个类永远不会被破坏。A
如果我们调用类的方法,我们通知调度队列完成执行闭包,那么所有这些类实例都将被破坏:stopWorking
ContentViewModel
A
......
Class A "7" is destructed.
Class A "6" is destructed.
Class A "5" is destructed.
Class A "4" is destructed.
Class A "3" is destructed.
Class A "2" is destructed.
Class A "1" is destructed.
Class A "0" is destructed.
这不是我想看到的。我希望这些类实例被破坏,只要没有其他人引用它们。这是 Clang 的 ARC 实现的错误吗?有什么优雅的方法可以解决这个问题吗?A
答: 暂无答案
评论
Foundation
Atomics
A
while
A
A