Swiftで変数を宣言するこれらのさまざまな方法は非常に興味深いと思いました。
// METHOD 1
var dogName: String = "Charlie"
// METHOD 2
var dogName: String {
return "Charlie"
}
// METHOD 3
let dogName = {
return "Charlie"
}
// METHOD 4
var dogName: String = {
return "Charlie"
}()
明らかに、メソッド3はletを宣言し、違いを知っています。しかし、なぜSwiftはメソッド4を許可するのですか?
これら4つの方法の違いは何ですか?
特に方法2と4の間でかなり混乱しています。さらに、方法3が方法4と比較して最後の括弧を失うのはなぜですか?
メソッド1は、文字列の標準変数宣言です。セッターとゲッターがあります
var dogName: String = "Charlie"
print(dogName) -> "Charlie"
dogName = "Rex" // Valid
方法2は、String型の計算されたプロパティであり、読み取り専用です。
var dogName: String {
return "Charlie"
}
print(dogName) -> "Charlie"
dogName = "Rex" // Invalid as property is read-only
メソッド3は、タイプ()-> Stringの読み取り専用プロパティであるため、基本的にラムダ関数です。
let dogName = {
return "Charlie"
}
print(dogName) -> "(Function)"
print(dogName()) -> "Charlie"
dogName = "Rex" // Invalid as property is read-only
方法4は、包含オブジェクトが初期化されたときに実行されるクロージャです。 var
なので、別の値に置き換えることができます
var dogName: String = {
return "Charlie"
}()
print(dogName) -> "Charlie"
dogName = "Rex" // Valid
そうは言っても、方法4はクロージャであるため、その中で他のコマンドを実行できます。この構成を使用してUILabelを初期化できる例を次に示します。
var dogNameLabel: UILabel = {
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 10, height: 10))
label.text = "Charlie"
return label
}()
違いを理解するために、より説明的な例を使用しましょう
これはクラスFoo
です
_class Foo {
var className = "Foo"
var dogName1 : String { return "Charlie " + className }
let dogName2 = {
return "My name is Charlie"
}
var dogName3 : String = {
var string = "My"
string += " name"
string += " is"
string += " Charlie"
print(string)
return string
}()
}
_
それでは、インスタンスを作成しましょう
_let foo = Foo()
_
メソッド1は、setterとgetterを使用して保存されたプロパティclassName
です。
_let name = foo.className
foo.className = "Bar"
print(foo.className) // "Bar"
_
メソッド2は、ゲッターのみを使用して計算されたプロパティ_dogName1
_です。値を動的に計算するために使用できます。
_print(foo.dogName1) // "Charlie Bar"
_
メソッドは、タイプ_dogName2
_のクロージャー_() -> String
_です。変数に割り当てて後で実行することができます
_let dogName = foo.dogName2 // assigns the closure but does not return the string.
print(dogName()) // "My name is Charlie"
_
メソッド4は変数_dogName3
_であり、すぐにクロージャを実行しますonceインスタンスFoo()
が初期化されると(print
行で証明されます) )
_print(foo.dogName3) // "My name is Charlie" but does not execute the closure and print `string` again
_
メソッド5もあります:_dogName3
_をlazy
として宣言した場合
_lazy var dogName3 : String = {
_
変数が最初にアクセスされるまで、クロージャは実行されません。利点は、クロージャでself
を使用することもできることです。これは、方法4では不可能です。
クイックテストを設定し、すべての名前をdogName1
、dogName2
、dogName3
、およびdogName4
に変更しました。次に、それぞれの名前を「スヌーピー」に変更するコードを追加しました。
#2と#3はどちらも読み取り専用であることがコンパイラーにわかっていたため、ビルドされませんでした。 (#2は、var
として宣言されているにもかかわらず、常に「Charlie」を返すように設定されています。
これらの2行をコメントアウトした後、2つのブレークポイントを設定しました。1つは初期化後、もう1つは更新を試みた後です。
最後に、それぞれのprint
を実行してみました。
ブレークポイント#1:#1と#4は「チャーリー」に設定され、#2は存在しない(初期化されていないため)、#3は初期化されているように見えますが、値がありません(まだ呼び出されていません。はい、最後の()
はメモリ内の何かを初期化します。
ブレークポイント#2:#1と#4が「スヌーピー」に更新されました。
print
:#1と#4の結果は「スヌーピー」、#2は「チャーリー」、#3は「(関数)」でした。
結論:#1と#4の間に違いはありません。それぞれがvar
と宣言され、デフォルトは「Charlie」です。 #2は、let
のために読み取り専用であり、常に「Charlie」を返します。 #3?インスタンスを作成し、変更しようとしてもビルドされませんが、使用方法がわかりません。
#3についてさらに追加する必要がある場合は、この回答を更新します。
方法1は非常に明白です。
方法2では、指定された変数のゲッターを定義しました。
メソッド3のdogNameのタイプは() -> String
ではなくString
。ここで初期化したのは、名前付きラムダです。
メソッド4では、文字列を返す無名(名前なし)ラムダ関数を使用して変数を初期化します。
3と4の違いは、4番目に()を使用してその関数を呼び出すため、Stringを取得し、以前は取得しないため、関数であるということです。