web-dev-qa-db-ja.com

Swift --Lazy Var vs.プログラムでビューを作成するときのLet(メモリの節約)

私は初心者で、LazyVarとLetの違いをある程度理解しています。 Lazy Varを特にImageViewsで使用すると、メモリ使用量が大幅に節約されることに気づきました。しかし、これまで見てきたチュートリアルやガイドでは、Lazy Varをあまり使用していないので、それが悪い習慣であり、何かを見落としているのではないかと疑っています。

少し調べてみたところ、Lazyは「スレッドセーフ」ではないことがわかりましたが、これが何を意味するのかわかりません。私は多くの賛否両論を見てきましたが、特に知識が非常に限られているため、結論を出すことはできません。

UIViewを作成するときにLazyVarとLetを使用しても問題ない(またはそれ以上の)のはいつですか?

lazy var profileImageView: UIImageView = {

    let imageView = UIImageView(image: #imageLiteral(resourceName: "page1"))
    imageView.translatesAutoresizingMaskIntoConstraints = false
    imageView.contentMode = .scaleAspectFit
    return imageView

}()
11
Jazure

lazy varを使用するかどうかは、コードとそのコンテキストによって異なります。それ自体は悪いことでも良いことでもありません。いつ適切かを決める必要があります。

それを決定する前に、lazy varが何であるかを知る必要があります。

lazy varとは何ですか?

レイジー初期化は、変数コンテンツの初期化(構築)が最初に使用されるまで遅延される概念です。このような変数への最初のアクセスは、初期化をトリガーします。遅延初期化変数を使用して変数が使用(必要)されるまでコンテンツは作成されないため、リソースを節約できます。

これは、遅延初期化の背後にある主要なドライブです。必要になるまで何かを作成しません。これは、何かをlazy varにするかどうかを決定するときに使用するロジックでもあります。

常に表示(必要)なビュー(またはその他のもの)を処理している場合、遅延初期化を使用してもほとんど意味がありません。一方、常に必要とは限らないインスタンスを処理している場合は、lazy varを使用することが正当化されます。

表示されたViewControllerでビューが常に表示されている場合は、怠惰にすることで多くのことを達成することはできません。特定の状況でのみ表示される場合(たとえば、ユーザーが折りたたまれたパネルを展開した場合)、遅延させるのは理にかなっています。これにより、View Controllerの読み込みが速くなり、デフォルトで使用するメモリが少なくなります。


スレッドセーフに関する限り、lazy varはSwiftではスレッドセーフではありません。

つまり、2つの異なるスレッドが同じlazy varに同時にアクセスしようとすると、そのような変数が初期化される前に、スレッドの1つが部分的に構築されたインスタンスにアクセスする可能性があります。

スレッドセーフの詳細については、次を参照してください。

Swift-怠惰なvarはスレッドセーフですか?

"lazy var"をスレッドセーフにする

10

lazy varを使用するもう1つの利点は、コードの可読性が向上することです。

あなたの例では、画像ビューに関連するコードは、初期化子、セットアップ関数、またはviewDidLoadに分散されるのではなく、グループ化されています。これにより、コードのリーダーがビューの構成方法を理解するためにコード内のさまざまな場所に足を踏み入れる必要がなくなるため、ローカルの推論が向上します。あなたの見解について学ぶために、彼らはその宣言にジャンプする必要があるだけです。

lazy varとしてマークされた初期化クロージャーはselfにアクセスでき、ターゲットアクションの追加や他の定数プロパティの参照など、クロージャー内でより多くの構成を実行できます。

クロージャを使用してプロパティ(特にビュー)をlazy varとして初期化することは良い習慣であると考えており、Swiftコミュニティでも人気が高まっているようです。

プロジェクトによっては、開発者の時間を節約する方が、システムメモリを節約するよりもはるかに価値があります。

5
nathangitter

レイジー変数を使用すると、逆説的な問題の回避策を提供できます。初期化が親ビューを参照するサブビューを持つカスタムビューを作成する必要があります。

たとえば、同じサイズの子UIScrollViewを含むUIViewのサブクラスを作成する場合、次を含むクラスを宣言することはできません。

var m_scrollView: UIScrollView

override init(frame: CGRect)
{
    m_scrollView = UIScrollView(frame: self.frame)
    super.init(frame: frame)
}

コンパイラは、super.initを呼び出す前に自分自身を参照していると文句を言います。しかし... super.initを呼び出す必要がありますafterすべてのメンバーが初期化されます。

この循環的な問題の解決策は、m_scrollViewを遅延させ、宣言で初期化することです。

lazy var m_scrollView = UIScrollView(frame: self.frame)
0
Oscar