Swift 结构内存泄漏
Swift Struct Memory Leak
我们正在尽可能地使用 Swift 结构。我们还使用了 RxSwift ,它有采用闭包的方法。当我们有一个结构创建一个引用 self 的闭包时,它会创建一个 strong reference cycle.
import Foundation
import RxSwift
struct DoesItLeak {
var someState: String = "initial value"
var someVariable: Variable<String> = Variable("some stuff")
let bag = DisposeBag()
mutating func someFoo() {
someVariable.subscribeNext { person in
self.someState = "something"
}
.addDisposableTo(bag)
}
}
我怎么知道的?如果我创建 100,000 个 DoesItLeak 对象并在每个对象上调用 someFoo(),我相信我有 100,000 个具有强引用循环的对象。换句话说,当我删除包含这些对象的 DoesItLeak 数组时,这些对象将保留在内存中。如果我不调用 someFoo(),就没有问题。
变量是 class。因此,我可以通过使用 xcode 的 Instruments' Allocations 并在 Variable< String >
中进行过滤来查看此内存问题
如果我尝试使用如下所示的 [weak self],我会收到编译器错误:
someVariable.subscribeNext { [weak self] person in
编译器错误是"weak cannot be applied to non-class type"
在 real/non-example 代码中,我们通过 self 访问方法和变量,这是一个内存问题。
如何在将 DoesItLeak 保持为结构的同时解决此内存问题?
感谢您的帮助。
As Darren put it in the comments: "" 我们不能让 DoesItLeak
成为结构并安全地解决强引用循环问题。
堆栈帧中存在类似结构的值类型。闭包和 classes 是引用类型。
正如 Strong Reference Cycles for Closures section 所说:
This strong reference cycle occurs because closures, like classes, are reference types.
由于该结构具有 Variable
class 并且引用 self
的闭包存储在 Variable
class 使用 subscribeNext
,它创建了强引用循环。请参阅 Automatic Reference Counting Apple 文档中的 "Resolving Strong Reference Cycles for Closures"。
现在不允许在可写上下文中通过转义闭包捕获自身的模式。 swift 编译器将发出错误 "Closure cannot implicitly capture a mutating self parameter"。如果上下文是只读的,则可以复制或共享 self 的值,并且在任何一种情况下都不会有引用循环。
对于仍然面临此问题的任何人。
[weak self]
是不可能的,因为 Struct 是 value type
而不是 Reference type
,所以没有这样的指针。
这里泄漏的主要问题是你试图访问完成块内的 Struct 属性 self.someState = something
这基本上会在分配时创建你的结构的新副本.
您不应在完成块内访问 Struct 属性。
您可以通过创建对闭包捕获的对象的弱引用来解决问题。
这是没有内存泄漏的示例:
import Foundation
import RxSwift
struct WithoutLeak {
var someState: String = "initial value"
var someVariable: Variable<String> = Variable("some stuff")
let bag = DisposeBag()
mutating func someFoo() {
weak let weakSomeState = someState // <-- create a weak reference outside the closure
someVariable.subscribeNext { person in
weakSomeState = "something" // <-- use it in the closure
}
.addDisposableTo(bag)
}
}
我们正在尽可能地使用 Swift 结构。我们还使用了 RxSwift ,它有采用闭包的方法。当我们有一个结构创建一个引用 self 的闭包时,它会创建一个 strong reference cycle.
import Foundation
import RxSwift
struct DoesItLeak {
var someState: String = "initial value"
var someVariable: Variable<String> = Variable("some stuff")
let bag = DisposeBag()
mutating func someFoo() {
someVariable.subscribeNext { person in
self.someState = "something"
}
.addDisposableTo(bag)
}
}
我怎么知道的?如果我创建 100,000 个 DoesItLeak 对象并在每个对象上调用 someFoo(),我相信我有 100,000 个具有强引用循环的对象。换句话说,当我删除包含这些对象的 DoesItLeak 数组时,这些对象将保留在内存中。如果我不调用 someFoo(),就没有问题。
变量是 class。因此,我可以通过使用 xcode 的 Instruments' Allocations 并在 Variable< String >
中进行过滤来查看此内存问题如果我尝试使用如下所示的 [weak self],我会收到编译器错误:
someVariable.subscribeNext { [weak self] person in
编译器错误是"weak cannot be applied to non-class type"
在 real/non-example 代码中,我们通过 self 访问方法和变量,这是一个内存问题。
如何在将 DoesItLeak 保持为结构的同时解决此内存问题?
感谢您的帮助。
As Darren put it in the comments: "DoesItLeak
成为结构并安全地解决强引用循环问题。
堆栈帧中存在类似结构的值类型。闭包和 classes 是引用类型。
正如 Strong Reference Cycles for Closures section 所说:
This strong reference cycle occurs because closures, like classes, are reference types.
由于该结构具有 Variable
class 并且引用 self
的闭包存储在 Variable
class 使用 subscribeNext
,它创建了强引用循环。请参阅 Automatic Reference Counting Apple 文档中的 "Resolving Strong Reference Cycles for Closures"。
现在不允许在可写上下文中通过转义闭包捕获自身的模式。 swift 编译器将发出错误 "Closure cannot implicitly capture a mutating self parameter"。如果上下文是只读的,则可以复制或共享 self 的值,并且在任何一种情况下都不会有引用循环。
对于仍然面临此问题的任何人。
[weak self]
是不可能的,因为 Struct 是value type
而不是Reference type
,所以没有这样的指针。这里泄漏的主要问题是你试图访问完成块内的 Struct 属性
self.someState = something
这基本上会在分配时创建你的结构的新副本.
您不应在完成块内访问 Struct 属性。
您可以通过创建对闭包捕获的对象的弱引用来解决问题。
这是没有内存泄漏的示例:
import Foundation
import RxSwift
struct WithoutLeak {
var someState: String = "initial value"
var someVariable: Variable<String> = Variable("some stuff")
let bag = DisposeBag()
mutating func someFoo() {
weak let weakSomeState = someState // <-- create a weak reference outside the closure
someVariable.subscribeNext { person in
weakSomeState = "something" // <-- use it in the closure
}
.addDisposableTo(bag)
}
}