Swift structsを使用できる場所で使用しようとしています。RxSwiftを使用しています。これには、クロージャを取得するメソッドがあります。selfを参照するクロージャを作成するstructがある場合 強い参照サイクル を作成します。
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()を呼び出さなくても問題ありません。
変数はクラスです。したがって、このメモリの問題は、xcodeのInstrumentsの割り当てを使用し、Variable <String>でフィルタリングすることで確認できます。
次のような[weak self]を使用しようとすると、コンパイラエラーが発生します。
someVariable.subscribeNext { [weak self] person in
コンパイラエラーは「クラス以外の型には弱いと適用できません」
実際の/例以外のコードでは、メソッドと変数にself経由でアクセスしますが、これはメモリの問題です。
DoesItLeak構造体を維持しながら、このメモリの問題を解決するにはどうすればよいですか?
ご協力いただきありがとうございます。
Darren をコメントに含めます: " DoesItLeak ca n't be a struct " DoesItLeak
を構造体にすることはできず、強力な参照サイクルの問題を安全に解決できます。
構造体などの値型はスタックフレームに存在します。クロージャーとクラスは参照型です。
クロージャーセクションの強力な参照サイクル がそれを置くように:
この強力な参照サイクルは、クラスと同様に、クロージャが参照型であるために発生します。
構造体にはVariable
classがあり、self
を参照するクロージャーはVariable
を使用してsubscribeNext
クラスに格納されるため、強い参照サイクルが作成されます。 自動参照カウント Appleドキュメンテーション)の「クロージャの強力な参照サイクルの解決」を参照してください。
まだこの問題に直面している人のために。
1)Structは[weak self]
ではなくvalue type
ではないため、Reference type
は使用できません。そのため、そのようなポインターはありません。
2)ここでのリークの主な問題は、完了ブロック内で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コンパイラは、「Closureは暗黙的に変化する自己パラメータをキャプチャできません」というエラーを発行します。コンテキストが読み取り専用の場合、自己の値はコピーまたは共有でき、どちらの場合も参照サイクルではありません。