web-dev-qa-db-ja.com

Swift Struct Memory Leak

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>でフィルタリングすることで確認できます。

Filtering By Variable

enter image description here

次のような[weak self]を使用しようとすると、コンパイラエラーが発生します。

someVariable.subscribeNext { [weak self] person in

コンパイラエラーは「クラス以外の型には弱いと適用できません」

実際の/例以外のコードでは、メソッドと変数にself経由でアクセスしますが、これはメモリの問題です。

DoesItLeak構造体を維持しながら、このメモリの問題を解決するにはどうすればよいですか?

ご協力いただきありがとうございます。

31
finneycanhelp

Darren をコメントに含めます: " DoesItLeak ca n't be a struct " DoesItLeakを構造体にすることはできず、強力な参照サイクルの問題を安全に解決できます。

構造体などの値型はスタックフレームに存在します。クロージャーとクラスは参照型です。

クロージャーセクションの強力な参照サイクル がそれを置くように:

この強力な参照サイクルは、クラスと同様に、クロージャが参照型であるために発生します。

構造体にはVariableclassがあり、selfを参照するクロージャーはVariableを使用してsubscribeNextクラスに格納されるため、強い参照サイクルが作成されます。 自動参照カウント Appleドキュメンテーション)の「クロージャの強力な参照サイクルの解決」を参照してください。

9
finneycanhelp

まだこの問題に直面している人のために。

1)Structは[weak self]ではなくvalue typeではないため、Reference typeは使用できません。そのため、そのようなポインターはありません。

2)ここでのリークの主な問題は、完了ブロック内でStructプロパティself.someState = somethingにアクセスしようとしていることです。これにより、基本的に、割り当て時に構造の新しいコピーが作成されます。

完了ブロック内でStructプロパティにアクセスしないでください。

3
helloWorld

クロージャによってキャプチャされたオブジェクトへの弱い参照を作成することで問題を解決できます。

メモリリークのない例を次に示します。

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)
    }
}
1
Nikaaner

書き込み可能なコンテキストでエスケープクロージャによって自己をキャプチャするパターンは現在許可されていません。 Swiftコンパイラは、「Closureは暗黙的に変化する自己パラメータをキャプチャできません」というエラーを発行します。コンテキストが読み取り専用の場合、自己の値はコピーまたは共有でき、どちらの場合も参照サイクルではありません。

0
Berik