1. ホーム
  2. スイフト

[解決済み】Swiftのオプション値とは何ですか?

2022-04-03 04:17:45

質問

から Appleのドキュメント :

を使用することができます。 iflet を併用することで、欠落している可能性のある値を処理することができます。これらの値は、オプショナルとして表現される。オプショナルな値は、値を含むか nil のように、値がないことを示す。クエスチョンマークを書く( ? のように、値の型の後に付けると、その値がオプションであることを示します。

なぜオプションの値を使用したいのですか?

どのように解決するのですか?

Swiftのオプショナルは、値か値なしを保持することができる型です。オプショナルは ? を任意の型に追加します。

var name: String? = "Bertie"

オプショナルは(ジェネリックと一緒に)Swiftのコンセプトの中で最も理解するのが難しいものの1つです。それらがどのように書かれ、使用されるかのために、それらが何であるかについて間違った考えを持つことは簡単です。上記のオプショナルを通常のStringを作成することと比較してみてください。

var name: String = "Bertie" // No "?" after String

構文からすると、オプショナルなStringは普通のStringと非常に似ているように見えます。しかし、そうではありません。オプショナルなStringは、何らかの"optional"設定がオンになっているStringではありません。また、特殊な文字列でもありません。Stringとoptional Stringは全く別のタイプです。

ここが一番大事なところです。オプショナルはコンテナの一種です。オプショナルな文字列は、文字列を含む可能性のあるコンテナです。オプショナルなIntは、Intを含む可能性のあるコンテナです。オプショナルは、小包のようなものだと考えてください。オプショナルを小包のようなものと考えてください。それを開ける(オプショナルという言葉では "unwrap" )前に、それが何かを含むのか何も含まないのか知ることはできません。

を見ることができます。 オプショナルがどのように実装されているか を入力し、⌘をクリックしてください。ここが定義の重要な部分です。

enum Optional<Wrapped> {
    case none
    case some(Wrapped)
}

オプショナルは単なる enum という2つの場合があります。 .none または .some . もし .some というように、関連する値があり、上の例では String hello"。オプショナルはジェネリックスを使用して、関連する値の型を指定します。オプショナルな文字列の型は、次のようなものではありません。 String であり Optional より正確には Optional<String> .

Swiftがオプションで行うことはすべて、コードの読み書きをより流暢にするための魔法です。残念ながら、これは実際に動作する方法を不明瞭にします。後でトリックのいくつかを見ていきます。

注意 これからオプショナル変数についてたくさんお話ししますが、オプショナル定数も作っても問題ありません。私は、作成される型がわかりやすいように、すべての変数にその型をマークしていますが、あなた自身のコードではその必要はありません。


オプショナルを作成する方法

オプショナルを作成するには ? の後に、ラップしたいタイプを記述します。どんなタイプでも、あなた自身のカスタムタイプでも、オプショナルにすることができます。型と ? .

var name: String? = "Bob" // Create an optional String that contains "Bob"
var peter: Person? = Person() // An optional "Person" (custom type)

// A class with a String and an optional String property
class Car {
var modelName: String // must exist
var internalName: String? // may or may not exist
}


オプションの使用

オプショナルを比較すると nil を使用して、値を持つかどうかを確認します。

var name: String? = "Bob"
name = nil // Set name to nil, the absence of a value
if name != nil {
    print("There is a name")
}
if name == nil { // Could also use an "else"
    print("Name has no value")
}

これは少し紛らわしいですね。これは、オプショナルがどちらか一方であることを暗示しています。nilかBob"のどちらかです。これは真実ではありません。オプショナルは他の何かに変化するわけではありません。nilと比較するのは、コードを読みやすくするためのトリックです。オプショナルが nil と等しい場合、これは単に enum に現在設定されているのが .none .


オプショナルのみnilにできる

オプションでない変数をnilに設定しようとすると、エラーが発生します。

var red: String = "Red"
red = nil // error: nil cannot be assigned to type 'String'

オプションのもう一つの見方は、通常のSwiftの変数を補完するものです。それらは、値を持つことが保証されている変数に対応するものです。Swiftは曖昧さを嫌う、慎重な言語です。ほとんどの変数は、非オプショナルとして定義されていますが、時にはそれが不可能な場合もあります。例えば、キャッシュやネットワークから画像をロードするビューコントローラを想像してみてください。ビューコントローラが作成された時点で、その画像を持っているかもしれませんし、持っていないかもしれません。image 変数の値を保証する方法はありません。この場合、この変数はオプションにする必要があります。これは nil で、画像が取得されると、オプショナルに値が入ります。

オプショナルを使うことで、プログラマーの意図が明らかになります。任意のオブジェクトがnilになる可能性があるObjective-Cと比較して、Swiftは、値が欠落する可能性があり、それが存在することが保証されているときについて明確であることが必要です。


オプショナルを使用するには、quot;unwrap" を使用します。

オプショナル String の代わりに使用することはできません。 String . オプショナル内部でラップされた値を使用するには、ラップを解除する必要があります。オプショナルをアンラップする一番簡単な方法は、オプショナルに ! の後、オプションの名前。これは "強制アンラップ" と呼ばれています。これは、オプショナル内の値を (元の型として) 返しますが、もしオプショナルが nil の場合、ランタイムクラッシュを引き起こします。アンラップする前に、値があることを確認する必要があります。

var name: String? = "Bob"
let unwrappedName: String = name!
print("Unwrapped name: \(unwrappedName)")

name = nil
let nilName: String = name! // Runtime crash. Unexpected nil.


オプションの確認と使用

オプショナルをアンラップして使用する前に必ずnilをチェックする必要があるため、このようなパターンがよくあります。

var mealPreference: String? = "Vegetarian"
if mealPreference != nil {
    let unwrappedMealPreference: String = mealPreference!
    print("Meal: \(unwrappedMealPreference)") // or do something useful
}

このパターンでは、値が存在するかどうかをチェックし、存在することが確認できたら、それを強制的にアンラップして一時的な定数にし、使用します。これは行うことが一般的であるため、Swift は "if let" を使用してショートカットを提供します。これは、"オプションのバインディングと呼ばれています。

var mealPreference: String? = "Vegetarian"
if let unwrappedMealPreference: String = mealPreference {
    print("Meal: \(unwrappedMealPreference)") 
}

これは、一時的な定数(または letvar のように、ifの中括弧の中だけをスコープとするものです。unwrappedMealPreference" や "realMealPreference" のような名前を使わなければならないのは負担なので、Swift では元の変数名を再利用し、ブラケットのスコープ内に一時的なものを作成することが可能です。

var mealPreference: String? = "Vegetarian"
if let mealPreference: String = mealPreference {
    print("Meal: \(mealPreference)") // separate from the other mealPreference
}

以下は、別の変数が使用されることを示すコードです。

var mealPreference: String? = "Vegetarian"
if var mealPreference: String = mealPreference {
    print("Meal: \(mealPreference)") // mealPreference is a String, not a String?
    mealPreference = "Beef" // No effect on original
}
// This is the original mealPreference
print("Meal: \(mealPreference)") // Prints "Meal: Optional("Vegetarian")"

オプショナルバインドは、オプショナルがnilに等しいかどうかを確認することで動作します。もしそうでなければ、オプショナルを与えられた定数にアンラップし、ブロックを実行します。Xcode 8.3 以降 (Swift 3.1) では、このようにオプショナルを表示しようとすると、無駄な警告が表示されます。オプショナルの debugDescription で黙らせることができます。

print("\(mealPreference.debugDescription)")


オプションは何のためにあるのですか?

オプショナルには2つの使用例があります。

  1. 失敗する可能性があるもの(何かを期待していたのに何も得られなかった)
  2. 今は何でもないが、後で何かになるかもしれないもの(その逆もしかり)

具体的な事例をいくつか紹介します。

  • のように、あってもなくてもいいプロパティ。 middleName または spouse の中に Person クラス
  • 配列の中から一致するものを探すような、値を返すか何も返さないかを選択できるメソッド
  • ファイルの内容を読み込もうとして(通常はファイルのデータを返す)ファイルが存在しない場合のように、結果を返すか、エラーを取得して何も返さないかのどちらかを選択できるメソッド
  • デリゲートプロパティ。常に設定する必要はなく、一般的には初期化後に設定する。
  • について weak プロパティがあります。それらが指し示すものを nil いつでも
  • メモリを再利用するために解放しなければならないかもしれない大きなリソース
  • 値が設定されたことを知る方法が必要な場合(データがまだ読み込まれていない >)別のdataLoadedを使用するのではなく、データ Boolean

Objective-Cにはオプショナルは存在しませんが、同等の概念として、nilを返すというものがあります。オブジェクトを返すことができるメソッドは、代わりにnilを返すことができる。これは有効なオブジェクトが存在しないことを意味し、何かがうまくいかなかったことを示すためによく使われます。これはObjective-Cのオブジェクトに対してのみ有効で、プリミティブや基本的なC型(enumやstructs)に対しては有効ではありません。Objective-Cでは、これらの値がないことを表すために、しばしば特別な型がありました ( NSNotFound というのは、本当は NSIntegerMax , kCLLocationCoordinate2DInvalid で無効な座標を表す。 -1 または何らかの負の値も使用されます)。コーダーはこれらの特別な値について知っていなければならないので、文書化し、それぞれのケースについて学習しなければなりません。もし、あるメソッドが nil をパラメータとして使用する場合は、ドキュメントを作成する必要があります。Objective-Cの場合。 nil は、すべてのオブジェクトがポインタとして定義されているのと同様に、ポインタであったが nil は特定の(ゼロ)アドレスを指していました。Swiftでは nil は、特定の型がないことを意味するリテラルである。


と比較すると nil

以前は、任意のオプショナルを Boolean :

let leatherTrim: CarExtras? = nil
if leatherTrim {
    price = price + 1000
}

最近のSwiftのバージョンでは leatherTrim != nil . これはなぜでしょうか?問題は Boolean はオプショナルで包むことができます。もし Boolean このように

var ambiguous: Boolean? = false

には、値がない場合と、値はあるがその値が false . Swift は曖昧さを嫌いますので、オプショナルをチェックするときは常に nil .

オプションの Boolean は?他のオプショナルと同じように .none の状態は、その値がまだ不明であることを示す可能性があります。ネットワークコールの相手側に何かあって、ポーリングに時間がかかるのかもしれません。オプションのブール値は、"とも呼ばれます。 3値ブール演算子 となります。


スウィフトの小技

Swift は、オプショナルが動作するようにいくつかのトリックを使用します。普通に見えるオプショナルコードのこれらの3行を考えてみましょう。

var religiousAffiliation: String? = "Rastafarian"
religiousAffiliation = nil
if religiousAffiliation != nil { ... }

これらの行はどれもコンパイルされないはずです。

  • 最初の行は、String リテラルを使用してオプションの String を設定しています。仮にこれが String 型が異なる
  • 2行目はオプションのStringにnilをセットしています。
  • 3行目は、オプションの文字列とnilを比較するもので、2つのタイプがあります。

これらの行を動作させるためのオプショナルの実装の詳細について説明します。


オプショナルを作成する

使用方法 ? を使用してオプショナルを作成することは、Swift コンパイラーによって有効化された構文上の糖分です。長い道のりを歩みたいのであれば、このようにオプショナルを作成することができます。

var name: Optional<String> = Optional("Bob")

これは Optional の最初のイニシャライザーです。 public init(_ some: Wrapped) これは、括弧の中で使われている型から、オプショナルに関連する型を推測します。

オプショナルを作成・設定するさらに長い方法です。

var serialNumber:String? = Optional.none
serialNumber = Optional.some("1234")
print("\(serialNumber.debugDescription)")


オプショナルを設定する nil

初期値なしのオプショナルを作成することも、初期値として nil (どちらも同じ結果になります)。

var name: String?
var name: String? = nil

オプショナルとイコールにできるようにする nil が有効なのは、プロトコル ExpressibleByNilLiteral (以前の名前は NilLiteralConvertible ). オプショナルは Optional の2番目のイニシャライザーです。 public init(nilLiteral: ()) . ドキュメントでは ExpressibleByNilLiteral をオプション以外のものに使うことは、コード中のnilの意味を変えてしまうことになるからです。

class Clint: ExpressibleByNilLiteral {
    var name: String?
    required init(nilLiteral: ()) {
        name = "The Man with No Name"
    }
}

let clint: Clint = nil // Would normally give an error
print("\(clint.name)")

同じプロトコルで、すでに作成されているオプショナルを nil . 推奨されませんが、nil リテラルのイニシャライザを直接使用することもできます。

var name: Optional<String> = Optional(nilLiteral: ())


オプショナルと比較する nil

オプショナルは、2つの特別な "==" と "!=" 演算子を定義しており、これらの演算子は Optional という定義があります。最初の == は、任意のオプショナルがnilと等しいかどうかをチェックすることができます。.noneに設定された2つの異なるオプショナルは、関連付けられた型が同じであれば常に等しくなります。nil と比較するとき、Swift は舞台裏で .none に設定された同じ関連型のオプショナルを作成し、比較のためにそれを使用します。

// How Swift actually compares to nil
var tuxedoRequired: String? = nil
let temp: Optional<String> = Optional.none
if tuxedoRequired == temp { // equivalent to if tuxedoRequired == nil
    print("tuxedoRequired is nil")
}

2番目の == 演算子を使うと、2 つのオプションを比較することができます。どちらも同じ型である必要があり、その型は次のように適合する必要があります。 Equatable (通常の "==" 演算子で比較できるようにするプロトコル)。Swiftは(おそらく)2つの値をアンラップして、直接比較します。それはまた、オプションの1つまたは両方が .none . との比較の区別に注意してください。 nil リテラルになります。

さらに、任意の Equatable 型と、その型をラップするオプショナルな

let numberToFind: Int = 23
let numberFromString: Int? = Int("23") // Optional(23)
if numberToFind == numberFromString {
    print("It's a match!") // Prints "It's a match!"
}

裏側では、Swift は比較の前に非オプションをオプショナルとしてラップしています。これはリテラルでも動作します ( if 23 == numberFromString { )

が2つあると言いました。 == という演算子がありますが、実は3つ目の演算子で nil 比較の左側にある

if nil == name { ... }


オプショナルの命名

オプショナル型と非オプショナル型を区別して命名するための Swift の規約はありません。人々は、それがオプショナルであることを示すために名前に何かを加えることを避け("optionalMiddleName"や、"possibleNumberAsString"など)、宣言でそれがオプションタイプであることを示すようにします。これは、オプショナル型の値を保持するための名前を付けたい場合に難しくなります。middleName"という名前は、それがString型であることを意味するので、そこからString値を取り出すと、しばしば "actualMiddleName" や "unwrappedMiddleName" や "realMiddleName" といった名前になってしまうことがあります。これを回避するには、オプショナルバインディングを使用し、変数名を再利用します。


公式定義

から プログラミング言語Swiftにおける基礎知識"。 :

<ブロッククオート

Swiftはまた、値がないことを扱うオプショナル型も導入しています。オプショナルは、「値があり、それは x に等しい」または「値が全くない」のどちらかを言います。オプショナルは、Objective-C のポインターで nil を使うのと似ていますが、クラスだけでなく、あらゆる型に対して機能します。オプショナルは Objective-C の nil ポインタよりも安全で表現力があり、Swift の最も強力な機能の多くの核心となるものです。

オプショナルは、Swiftが型安全な言語であることを示す一例です。Swift は、あなたのコードが扱うことができる値の型について明確にすることを助けます。コードの一部が String を期待する場合、型安全性は、間違って Int を渡すことを防ぎます。これによって、開発プロセスのできるだけ早い段階でエラーを発見し、修正することができます。


最後に、1899年に書かれたオプションについての詩を紹介します。

昨日、階段の上で

そこにいない人に会った

今日もいなかった

♪ I wish, I wish he'd go away


アンティゴニッシュ


その他のリソース