web-dev-qa-db-ja.com

レイジーヴァールvsレット

SwiftのいくつかのプロパティにLazy初期化を使用したい。私の現在のコードは次のようになります。

lazy var fontSize : CGFloat = {
  if (someCase) {
    return CGFloat(30)
  } else {
    return CGFloat(17)
  }
}()

問題は、fontSizeが設定されると、決して変更されないことです。だから私はこのようなことをしたかった:

lazy let fontSize : CGFloat = {
  if (someCase) {
    return CGFloat(30)
  } else {
    return CGFloat(17)
  }
}()

それは不可能です。

これだけが機能します:

let fontSize : CGFloat = {
  if (someCase) {
    return CGFloat(30)
  } else {
    return CGFloat(17)
  }
}()

だから-私は遅延ロードされるが、決して変わらないプロパティが欲しい。それを行う正しい方法は何ですか? letを使用して、怠zyなinitを忘れますか?または、lazy varを使用して、プロパティの定数の性質を忘れる必要がありますか?

45
YogevSitton

これは Xcode 6.3 Beta/Swift 1.2リリースノート

すぐに初期化する必要がなくなるように定数を一般化しました。新しい規則は、使用する前にlet定数(varなど)を初期化する必要があり、初期化のみが可能であることです。初期化後に再割り当てまたは変更されません。

これにより、次のようなパターンが可能になります。

let x: SomeThing
if condition {
    x = foo()
} else {
    x = bar()
}

use(x)

以前は、変異が発生していなくても、varの使用が必要でした。 (16181314)

これにイライラしたのはあなただけではなかったようです。

24
Chris Conover

Swift book 次のメモがあります

インスタンスの初期化が完了するまで初期値は取得されない可能性があるため、遅延プロパティを変数として(varキーワードを使用して)常に宣言する必要があります。定数プロパティは、初期化が完了する前に常に値を持っている必要があるため、遅延として宣言できません。

これは、オブジェクトの初期化が完了する前にすべての定数の保存されたプロパティが計算されるため、言語を実装するコンテキストでは意味があります。 letと一緒に使用すると、lazyのセマンティクスが変更された可能性があるという意味ではありませんが、まだ実行されていないため、varが唯一のオプションです。この時点でlazy

あなたが提示した2つの選択肢に関しては、効率に基づいてそれらを選択します:

  • プロパティの値へのアクセスがめったに行われず、事前に計算するのに費用がかかる場合は、var lazy
  • 値が20..30%以上のケースでアクセスされる場合、または計算が比較的安価な場合、letを使用します

注:コードをさらに最適化して、条件をCGFloat初期化子にプッシュします。

let fontSize : CGFloat = CGFloat(someCase  ? 30 : 17)
22
dasblinkenlight

Dasblinkenlightが指摘しているように、レイジープロパティは常にSwiftの変数として宣言する必要があります。ただし、プロパティを読み取り専用にすることは可能です。そのため、エンティティが定義されたソースファイル内からのみ変更できます。これは、 "lazy let"を定義するのに最も近い方法です。

private(set) lazy var fontSize: CGFloat = {
    if someCase {
        return 30
    } else {
        return 17
    }
}()
10
sdduursma