モバイル開発の経験(Swiftの使用を含む)にもかかわらず、Swiftのオプションに基づいて何をすべきかを定期的に知っていましたが、 なぜそうなのか明確に理解できませんでした。 気を散らし、ドキュメントを掘り下げなければなりませんでした-「限界ノート」の数は、憂鬱な周期性で補充されました。 ある時点で、彼らは大衆に達し、私はそれらを単一の包括的なガイドにまとめることにしました。 可能な限り詳細にトピックを開示する試みが行われたため、資料は非常に膨大であることが判明しました。 この記事は、Swiftの初心者開発者とObjective-Cの世界の経験豊富な専門家の両方に役立ちます。Objective-Cの世界では、Objective-Cが新しい何かを見つける可能性がゼロではありません。 そして、それが見つからない場合は、コメントに独自の新しいものを追加し、誰もが利益を得ます。
オプションとは何ですか?
オプションは、変数の値が存在しない状況を処理するための便利なメカニズムです。 値は、使用されている場合にのみ使用されます。
nilのチェックがあるときにオプションが必要なのはなぜですか?
まず、 nil
等価性/不等価性チェックは、 null許容型にのみ適用され、プリミティブ型、構造体、および列挙には適用されません。 プリミティブ型の変数に値がないことを示すには、 NSNotFoundなどの特別な値を入力する必要があります。
NSNotFoundは、特別な値と見なされる必要があるだけでなく、変数の有効な値のセットに入らないようにするためにも必要です。 NSNotFoundがNSIntegerMaxと等しいと見なされるという事実により、状況は複雑になります。 プラットフォーム(32ビット/ 64ビット)によって意味が異なる場合があります。 これは、 NSNotFoundをファイルやアーカイブに直接書き込むことも、 分散オブジェクトで使用することもできないことを意味します。
したがって、この変数のユーザーは、特別な値が可能なことを考慮する必要があります。 Swiftでは、プリミティブ型でもオプションのスタイルで使用できます。つまり、値がない可能性があることを明示的に示します。
第二に、コンパイル段階で明示的なオプションがチェックされるため、実行時のエラーの数が減ります。 Swiftのオプション変数は、オプション変数と同じ方法で使用することはできません(暗黙的に取得されたオプションを除き、詳細については暗黙的なアンラッピングを参照してください )。 オプションを強制的に通常の値に変換するか、 if let
、 guard let
および??
などの特別な変換イディオムを使用する必要があります。 。 Swiftのオプションは、検証だけでなく、 型 理論の オプション型のパラダイム全体を実装します。
第三に、オプションはnil
チェックよりも構文的に簡潔です。これは、オプションの呼び出しチェーン、いわゆるOptional Chainingで特に顕著です。
どのように機能しますか?
Swiftのオプションは、 nil
または特定のタイプのオブジェクトを含むことができる特別なコンテナオブジェクトです。このオブジェクトは、このコンテナが宣言されたときに指定されます。 これらの2つの状態は、それぞれ用語NoneおよびSomeで示されます。 オプション変数の作成中に割り当てられた値が指定されていない場合、デフォルトでnil
割り当てられます。
ドキュメントでは、明示的な割り当てがない場合のデフォルト値は言及されていませんが、オプションはラップされた値またはnil(valueがない場合)を表す と言われています。 オプション変数が明示的な割り当てなしで宣言された場合(一部は割り当てられなかったものもあります)、論理的にはNoneが暗黙的に割り当てられます-オプションには3番目の「初期化されていない」状態はありません。
タイプ名とトークンの組み合わせでオプションが宣言されています?
。 エントリはInt?
-これはコンテナ宣言であり、そのインスタンスには内部nil
(状態None Int )またはタイプInt
(状態Some Int )の値が含まれる場合があります。 それがInt?
変換するときの理由Int?
Int
では、 castの代わりにunwrappingという用語を使用します。 オプションの「コンテナ」の本質を強調しています。 Swift nil
トークンは、任意のオプションに割り当てることができるNone状態を示します。 これにより、論理的に、オプションではない変数にnil
(状態None )を割り当てることができなくなります。
実際、オプションはシステム列挙です:
public enum Optional<Wrapped> : ExpressibleByNilLiteral { /// The absence of a value. /// /// In code, the absence of a value is typically written using the `nil` /// literal rather than the explicit `.none` enumeration case. case none /// The presence of a value, stored as `Wrapped`. case some(Wrapped) /// Creates an instance that stores the given value. public init(_ some: Wrapped) ... /// Creates an instance initialized with `nil`. /// /// Do not call this initializer directly. It is used by the compiler // when you initialize an `Optional` instance with a `nil` literal. public init(nilLiteral: ()) ... }
Optional
列挙には、 .none
とsome(Wrapped)
2つの状態があります。 レコードがWrapped?
プリプロセッサ( Swiftの型システム )によって処理され、 Optional<Wrapped>
変換されます。 次のエントリは同等です。
var my_variable: Int?
var my_variable: Optional<Int>
実際、 nil
トークンはOptional.none
、つまり 次のエントリは同等です。
var my_variable: Int? = nil
var my_variable: Optional<Int> = Optional.none
var my_variable = Optional<Int>.none
Optional
列挙には、2つのコンストラクターがあります。 最初のコンストラクタinit(_ some: Wrapped)
は、対応する型の値を入力として受け入れます。 次のエントリは同等です。
var my_variable = Optional(42) // .some- Int
var my_variable = Optional<Int>(42) // Int
var my_variable = Int?(42) // Int
var my_variable: Int? = 42 // Int
var my_variable = Optional.some(42) // Int
var my_variable = Optional<Int>.some(42) //
2番目のコンストラクタinit(nilLiteral: ())
は、 ExpressibleByNilLiteral
プロトコルの実装です
public protocol ExpressibleByNilLiteral { /// Creates an instance initialized with `nil`. public init(nilLiteral: ()) }
オプションの変数を状態.none
で初期化します。 このコンストラクターはコンパイラーによって使用されます。 ドキュメントによると、直接呼び出すことは推奨されていません
var test = Optional<Int>(nilLiteral: ()) //
空のVoid ()
タプルをnil
変換することnil
いくぶん明白でnil
ないため、これは論理的です。
代わりにこのコンストラクタを使用してください
var my_variable: Int? = nil // var my_variable: Int? = Optional.none
または明示的な割り当てをまったく使用しない
var my_variable: Int?
デフォルトではnil
が割り当てられるためです。
Optional<Wrapped>
列挙には、unsafelyUnwrappedプロパティも含まれます。このプロパティは、オプションのオプションの.some
への読み取りアクセスを提供.some
ます。
public enum Optional<Wrapped> : ExpressibleByNilLiteral { ... /// The wrapped value of this instance, unwrapped without checking whether /// the instance is `nil`. public var unsafelyUnwrapped: Wrapped { get } }
オプションが.none
状態の場合、 .none
にアクセスすると、プログラムが深刻にクラッシュします。
デバッグモードデバッグビルド-なしでは、ランタイムエラーが発生します。
_fatal error: unsafelyUnwrapped of nil optional_
リリースビルドで最適化されたビルド-Oでは 、ランタイムエラーまたは未定義の動作が発生します。 より安全な操作はForce Unwrapping (またはExplicit Unwrapping ) !
トークンで示される.some
値.some
抽出を.some
します!
。 Force Unwrappingを.none
状態のオプションに適用すると、ランタイムエラーが発生します。
_fatal error: unexpectedly found nil while unwrapping an Optional value_
let my_variable1 = Int?(42) // 42, Optional Int let my_value1A = my_variable1! // 42, Int let my_value1B = my_variable1.unsafelyUnwrapped // 42, Int let my_variable2 = Int?.none // nil, Optional Int let my_value2A = my_variable2! // // -Onone, -O let my_value2B = my_variable2.unsafelyUnwrapped
使用のイディオム
通常の2状態の列挙を使用することはほとんど意味がありません。 同様のメカニズムを自分で実装することは非常に可能です:対応する値の2つの状態とコンストラクターを使用して列挙型を作成し、 Force Unwrappingの接尾辞演算子を追加し(たとえば、 ここで行われます )、 nil
と比較する機能を追加するか、独自のnil
とtを作成します.d。 オプションは、その使用が外国ではなく自然であるように、言語自体に直接統合する必要があります。 もちろん、このような統合は「シンタックスシュガー」と見なすことができますが、その上にコードを記述(および読み取り)するための高レベル言語は簡単で快適でした。 Swiftでオプションを使用するには、エラーを減らし、コードをより簡潔にするために、いくつかのイディオムまたは特別な言語構成が必要です。 そのようなイディオムには、 暗黙的なアンラッピング 、 オプションのチェーン 、 Nil-Coalescing、およびオプションのバインディングが含まれます。
暗黙的なアンラッピング
Force Unwrappingを安全に使用するには、たとえばif条件でnil
を事前に確認する必要があります。
// getOptionalResult() nil let my_variable: Int? = getOptionalResult() // Optional Int if my_variable != nil { // my_value .some- getOptionalResult() let my_value = my_variable! } else { // let my_value = my_variable! }
プログラムの構造から、変数は技術的にはオプションであることが明らかな場合もありますが、最初の使用時には常に.some
状態になっています。 nil
はありません。 オプションを非オプションのコンテキストで使用するには(たとえば、オプションの型のパラメーターを使用して関数に渡す)、退屈で面倒な事前チェックでForce Unwrappingを常に使用する必要があります。 これらの場合、暗黙的な取得オプション-Implicitly Unwrapped Optionalを使用できます。 暗黙的な検索オプションは、タイプ名とトークンの組み合わせで宣言されます!
:
let my_variable1: Int? = 42 // Optional Int let my_variable2: Int! = 42 // Implicitly Unwrapped Optional Int var my_variable3: Int! = 42 // Implicitly Unwrapped Optional Int ... my_variable3 = nil // - nil ... func sayHello(times:Int) { for _ in 0...times { print("Hello!") } } sayHello(times: my_variable1!) // sayHello(times: my_variable1) // sayHello(times: my_variable2!) // , sayHello(times: my_variable2) // sayHello(times: my_variable3) //
sayHello(times: my_variable2)
への呼び出しでは、 my_variable2
からの値42
抽出は暗黙的にのみ実行されます。 暗黙的に取得されたオプションを使用すると、コードが読みやすくなります-気を散らす感嘆符はありません(おそらく、読者は最初にチェックせずにForce Unwrappingを使用することを心配するでしょう)。 実際には、エラーの可能性を高めるアンチパターンです。 暗黙的に取得されたオプションは、オプションのコンテキストでオプションが使用されているという事実にコンパイラーに「目を閉じさせる」ことを強制します。 コンパイル時に検出できるエラー( sayHello(times: my_variable1)
は実行時にのみ表示されます( sayHello(times: my_variable3)
呼び出す)。 明示的なコードは、暗黙的なコードよりも常に優れています。 感嘆符をなくすためだけでなく、このようなコードセキュリティの低下が必要であると想定するのは論理的です。
暗黙的に取得されたオプションを使用self
と、コンストラクターでself
を使用してプロパティを初期化すると同時に、次のことができます。
- 2段階の初期化の規則に違反しないでください(コンストラクターでは、
self
にアクセスする前にすべてのプロパティを初期化する必要があります)。そうでない場合、コードは単にコンパイルされません。 - 必要のないプロパティでは、過度のオプションを避けます(その意味では、プロパティの値をなくすことはできません)。
コンストラクターでself
を使用してプロパティを初期化する良い例は、 ドキュメントに記載されています 。
class Country { let name: String var capitalCity: City! init(name: String, capitalName: String) { self.name = name self.capitalCity = City(name: capitalName, country: self) } } class City { let name: String unowned let country: Country init(name: String, country: Country) { self.name = name self.country = country } } var country = Country(name: "Canada", capitalName: "Ottawa") print("\(country.name)'s capital city is called \(country.capitalCity.name)") // Prints "Canada's capital city is called Ottawa"
この例では、 CountryクラスとCityクラスのインスタンスには、初期化が完了するまでに相互にフレンドリンクが必要です。 各国には首都がなければならず、各首都には国がなければなりません。 これらの接続はオプションではなく、無条件です。 country
オブジェクトを初期化するプロセスでは、 capitalCity
プロパティを初期化する必要があります。 capitalCity
を初期化するには、 Cityクラスのインスタンスを作成する必要があります。 Cityコンストラクターには、対応するCountryインスタンスがパラメーターとして必要です。 self
へのアクセスが必要です。 問題は、 Countryインスタンスがまだ完全に初期化されていないことです。 self
は使用できません。
このタスクにはエレガントなソリューションがありますcapitalCity
、暗黙的に取得される可変オプションであるcapitalCity
宣言されています。 任意の可変オプションと同様に、 capitalCity
はデフォルトでnil
状態に初期化されます。つまり、 Cityコンストラクターが呼び出されるまでに、 country
オブジェクトのすべてのプロパティはすでに初期化されています。 2段階の初期化の要件が満たされ、 Countryコンストラクターは第2フェーズにありますself
をCityコンストラクターに渡すことができます。 capitalCity
は暗黙的なオプションです。 追加せずにオプションのコンテキストでアクセスできます!
。
暗黙的に取得されたオプションを使用することの副作用は、「組み込み」 assert
です。何らかの理由でcapitalCity
がnil
状態のままになると、ランタイムエラーとプログラムのクラッシュが発生します。
暗黙的に取得された@IBOutlet
の正当な使用のもう1つの例は@IBOutlet
命令です。その使用のコンテキストは、変数が最初の呼び出し時に.some
値を自動的に割り当てられることを意味します。 そうでない場合、実行時エラーが発生します。 Interface Builderの自動コード生成は、暗黙的な@IBOutlet
形式で@IBOutlet
してプロパティを作成し@IBOutlet
。 この動作が受け入れられない場合、 @IBOutlet
を持つプロパティを明示的なオプションとして宣言し、常に.none
値を明示的に処理.none
ます。 原則として、誤って@IBOutlet
-propertyが@IBOutlet
れた場合に長時間のデバッグを行うよりも、すぐに「フォール」を取得することをお@IBOutlet
ます。
オプションの連鎖
オプションのチェーンは、各リンクがオプションを返す連続したチェーン呼び出しのプロセスです。 プロセスは、 nil
状態にある最初のオプションで中断されます。この場合、呼び出しチェーン全体の結果もnil
ます。 チェーン内のすべてのリンクが.some
状態にある場合、結果の値は最後の呼び出しの結果ではオプションになります。 チェーンリンクの形成にトークンが使用されています?
、オプションを返す呼び出しの直後に配置されます。 チェーンリンクには、オプションを返す任意の操作を使用できます。ローカル変数へのアクセス(最初のリンクとして)、プロパティとメソッドの呼び出し、インデックスによるアクセス。
オプションのシェーニングは、常に左から右に順番に機能します。 次の.some
は前のリンクの値であり、チェーンの結果の値は常にオプションです。 チェーンは、次のルールに従って機能します。
- 最初のリンクはオプションである必要があります。
- トークンの後
?
次のリンクがあるはずです。 - リンクが
.none
状態の場合、チェーンは呼び出しプロセスを中断し、nil
を返します。 - リンクが
.some
状態にある場合、チェーンは.some
リンク.some
を次のリンク(ある場合)に.some
ます。 - 最後のリンクの結果がオプションの場合、チェーンはこのオプションを返します。
- 最後のリンクの結果がオプションでない場合、チェーンはこの結果を返し、オプションで「ラップ」します(計算の結果
.some
返されたオプションの.some
割り当てられます)。
// : — `country.mainSeaport?`, country.mainSeaport?.nearestVacantPier?.capacity // , `?` let mainSeaport = country.mainSeaport? // `nil` country = Country(name: "Mongolia") let capacity = country.mainSeaport?.mainPier?.capacity // — country = Country(name: "Finland") let nearestVacantPier = country.mainSeaport?.nearestVacantPier // — , capacity // country = Country(name: "Finland") let capacity = country.mainSeaport?.nearestVacantPier?.capacity
オプションの呼び出しチェーンとネストされたオプションを区別することが重要です。 ネストされたオプションは.some
あるオプションの.some
が別のオプション.some
ある場合に形成されます。
let valueA = 42 let optionalValueA = Optional(valueA) let doubleOptionalValueA = Optional(optionalValueA) let tripleOptionalValueA = Optional(doubleOptionalValueA) let tripleOptionalValueB: Int??? = 42 // `?` let doubleOptionalValueB = tripleOptionalValueB! let optionalValueB = doubleOptionalValueB! let valueB = optionalValueB! print("\(valueA)") // 42 print("\(optionalValueA)") // Optional(42) print("\(doubleOptionalValueA)") // Optional(Optional(42)) print("\(tripleOptionalValueA)") // Optional(Optional(Optional(42))) print("\(tripleOptionalValueB)") // Optional(Optional(Optional(42))) print("\(doubleOptionalValueB)") // Optional(Optional(42)) print("\(optionalValueB)") // Optional(42) print("\(valueB)") // 42
オプションのシェーニングでは、返されるオプションのネストレベルは増加しません。 それにもかかわらず、これは、リンクの結果の値がいくつかのレベルのネストを持つオプションである場合の状況を除外しません。 このような状況では、チェーンを継続するには登録する必要があります?
ネストレベルの数に等しい量:
let optionalAppDelegate = UIApplication.shared.delegate let doubleOptionalWindow = UIApplication.shared.delegate?.window let optionalFrame = UIApplication.shared.delegate?.window??.frame // '?' print("\(optionalAppDelegate)") // Optional( ... ) print("\(doubleOptionalWindow)") // Optional(Optional( ... )) print("\(optionalFrame)") // Optional( ... )
一般的に、トークンを使用してすべてのレベルのネストを「デプロイ」する必要はありません?
それらのいくつかは、強制抽出によって置き換えることができます!
、チェーン内の「暗黙的な」リンクの数を減らします。 別の質問は、これが理にかなっているかどうかです。
UIApplication.shared.delegate?.window??.frame
チェーンは、実際には4つのリンクで構成されています: UIApplication.shared.delegate?
.frame
と2つのリンクを1回の呼び出しで結合します。 。 2番目の「二重」リンクは、オプションのネストの2番目のレベルで表されます。
この例の重要な機能は、前の例のdoubleOptionalValue
の形成方法とは異なり、double optionalを形成する特別な方法でもあります。 UIApplication.shared.delegate!.window
は、オプションが返されるオプションのプロパティです。 プロパティのオプションとは、プロパティから返されるオプションの.some
だけでなく、プロパティ自体が存在しない可能性があることを意味します。 オプションのプロパティは、他のすべてのプロパティと同様に、オプションのプロパティだけでなく、任意のタイプを返すことができます。 この種のオプションは、 optional
修飾子を使用して@ objcプロトコルで形成されoptional
。
public protocol UIApplicationDelegate : NSObjectProtocol { ... @available(iOS 5.0, * ) optional public var window: UIWindow? { get set } // optional ... }
オプションのプロパティとメソッド(それ以外の場合はオプションの要件)を備えたプロトコルでは、オプションの要件ごとおよびプロトコル自体に対して@objc
修飾子@objc
指定されます。 この要件は、上の例のUIApplicationDelegateプロトコルには適用されません。 Objective-CのシステムライブラリからSwiftに変換されます。 そのようなプロトコルを受け入れるオブジェクトで未実現のオプション要件を呼び出すと、対応するタイプのオプションが.none
状態で.none
ます。 実装されたオプション要求を呼び出すと、対応するタイプのオプションが.some
状態で.some
ます。 したがって、オプションのプロパティとメソッドは、 オプションのshainingとは対照的に、返されるオプションのネストレベルを増やします。 オプションのメソッドは、プロパティと同様に、オプションで完全に「ラップ」されます。メソッド全体は、戻り値だけでなく.some
値に配置されます。
@objc public protocol myOptionalProtocol { @objc optional var my_variable: Int { get } @objc optional var my_optionalVariableA: Int? { get } // : // @objc Int?, .. Int // @objc optional var my_optionalVariableB: UIView? { get } @objc optional func my_func() -> Int @objc optional func my_optionalResultfuncA() -> Int? // : // @objc Int?, .. Int // @objc optional func my_optionalResultfuncB() -> UIView? @objc optional init(value: Int) // : // optional } @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate, myOptionalProtocol { var window: UIWindow? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { let protocolAdoption = self as myOptionalProtocol // Optional<Int> print("\(type(of: protocolAdoption.my_variable))") // Optional<Optional<UIView>> print("\(type(of: protocolAdoption.my_optionalVariableB))") // Optional<() -> Int> print("\(type(of: protocolAdoption.my_func))") // Optional<Int> print("\(type(of: protocolAdoption.my_func?()))") // Optional<() -> Optional<UIView>> print("\(type(of: protocolAdoption.my_optionalResultfuncB))") // Optional<UIView> print("\(type(of: protocolAdoption.my_optionalResultfuncB?()))") return true } }
@objc
- , , Swift Objective-C:
- , Objective-C,
@objc
(.. ); -
optional
-init
; - c
@objc
— .
Force Unwrapping , Force Unwrapping .none
.
Nil-Coalescing
Nil-Coalescing .some
- , .some
, , .none
. Nil-Coalescing , if else
, , ?
:
let optionalText: String? = tryExtractText() // let textA: String if optionalText != nil { textA = optionalText! } else { textA = "Extraction Error!" } // , let textB = (optionalText != nil) ? optionalText! : "Extraction Error!" // let textC = optionalText ?? "Extraction Error!"
.some
- . :
let optionalText: String?? = tryExtractOptionalText() let a = optionalText ?? Optional("Extraction Error!")
:
let wayA: Int? = doSomething() let wayB: Int? = doNothing() let defaultWay: Int = ignoreEverything() let whatDo = wayA ?? wayB ?? defaultWay
Optional Binding
Optional Binding , .some
-, , ( ). Optional Binding if
, while
guard
.
Optional Binding , , .
, , nil
, nil
. if
, true , nil
false :
var my_optionalVariable: Int? = 42 // , my_variable "" .some- my_optionalVariable if let my_variable = my_optionalVariable { print("\(my_variable)") // 42 } my_optionalVariable = nil // , my_variable if let my_variable = my_optionalVariable { print("\(my_variable)") } else { print("Optional variable is nil!") // Optional variable is nil! }
, true , . - nil
.some
- . true .some
- "" ( Optional Binding ).
if
true , , false . , , .some
-, false ( .none
) . guard
:
let my_optionalVariable: Int? = extractOptionalValue() // let my_variableA: Int if let value = my_optionalVariable { my_variableA = value } else { return } print(my_variableA + 1) // guard let my_variableB = my_optionalVariable else { return } print(my_variableB + 1)
Swift (, ) as
. , (, ), as!
, as?
。 Force Unwrapping , .. , nil
:
class Shape {} class Circle: Shape {} class Triangle: Shape {} let circle = Circle() let circleShape: Shape = Circle() let triangleShape: Shape = Triangle() circle as Shape // 42 as Float // circleShape as Circle // circleShape as! Circle // circle triangleShape as! Circle // circleShape as? Circle // Optional<Circle> triangleShape as? Circle // nil
, as?
, Optional Binding :
class Shape {} class Circle: Shape {} class Triangle: Shape {} let circleShape: Shape = Circle() let triangleShape: Shape = Triangle() // , if let circle = circleShape as? Circle { print("Cast success: \(type(of: circle))") // Cast success: (Circle #1) } else { print("Cast failure") } // , if let circle = triangleShape as? Circle { print("Cast success: \(type(of: circle))") } else { print("Cast failure") // Cast failure }
map flatMap
map
flatMap
Swift, Optional :
public enum Optional<Wrapped> : ExpressibleByNilLiteral { ... /// Evaluates the given closure when this `Optional` instance is not `nil`, /// passing the unwrapped value as a parameter. /// /// Use the `map` method with a closure that returns a nonoptional value. public func map<U>(_ transform: (Wrapped) throws -> U) rethrows -> U? /// Evaluates the given closure when this `Optional` instance is not `nil`, /// passing the unwrapped value as a parameter. /// /// Use the `flatMap` method with a closure that returns an optional value. public func flatMap<U>(_ transform: (Wrapped) throws -> U?) rethrows -> U? ... }
.some
- , . nil
, nil
. map
flatmap
-: flatMap
nil
(), map
:
let my_variable: Int? = 4 let my_squareVariable = my_variable.map { v in return v * v } print("\(my_squareVariable)") // Optional(16) let my_reciprocalVariable: Double? = my_variable.flatMap { v in if v == 0 { return nil } return 1.0 / Double(v) } print("\(my_reciprocalVariable)") // Optional(0.25)
map
flatmap
, if
guard
, , :
let dateFormatter = DateFormatter() dateFormatter.dateFormat = "dd MMM yyyy" let date: Date? = extractOptionalDate() // let dateStringA: String if date != nil { dateStringA = dateFormatter.string(from: date!) } else { dateStringA = "Unknown date" } // , let dateStringB = (date == nil ? nil : dateFormatter.string(from: date!)) ?? "Unknown date" // , ( map ) let dateStringC = date.map(dateFormatter.string) ?? "Unknown date"
, . , .some
- , map
flatmap
:
// Optional Binding func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { if let cell = sender as? UITableViewCell, let indexPath = tableView.indexPathForCell(cell) { let item = items[indexPath.row] } } // 3 : // 1) ; // 2) flatMap ; // 3) Optional Binding flatMap. func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { if let indexPath = (sender as? UITableViewCell).flatMap(tableView.indexPathForCell) { let item = items[indexPath.row] } }
func someThrowingFunction() throws -> Int { // ... } // let y: Int? do { y = try someThrowingFunction() } catch { y = nil } // let x = try? someThrowingFunction()
x
y
, , someThrowingFunction()
. , try?
, as?
。 try!
, . , :
// , loadImage // photo ( x y) let photo = try! loadImage(atPath: "./Resources/John Appleseed.jpg")
Objective-C
Objective-C . nil
Objective-C , .. nil
. Swift nil
.none
, nil
, Xcode 6.3 Objective-C Swift . Xcode 6.3 Objective-C nullability annotations :
@interface myObject : NSObject @property (copy, readonly) NSArray * _Nullable myValuesA; @property (copy, readonly) NSString * _Nonnull myStringA; @property (copy, readonly, nullable) NSArray * myValuesB; @property (copy, readonly, nonnull) NSString * myStringB; @end
nullable
( _Nullable
), nonnull
( _Nonnull
), null_unspecified
null_resettable
. Nullability -a , . NS_ASSUME_NONNULL_BEGIN
NS_ASSUME_NONNULL_END
. , Objective-C ( , , nil
nonnull ).
Objective-C Swift :
-
NS_ASSUME_NONNULL_BEGIN
NS_ASSUME_NONNULL_END
() ; -
nonnull
_Nonnull
() ; -
nullable
_Nullable
; -
null_resettable
; - nullability -a
null_unspecified
( ) .
Swift Objective-C :
-
.none
, NSNull ; -
.some
,.some
-.
,
!
, :
- ;
- ;
-
as!
; -
try!
。
単項論理否定演算子は!
、異なるコンテキストを参照するため、考慮されません。
?
, :
- ;
- optional chaining ;
-
as?
; -
nil
try?
。
三項条件演算子は?
、異なるコンテキストを参照するため、考慮されません。
??
:
- Optional haining ;
- Nil-Coalescing .
おわりに
, . ++ Java "", . "" , , "" . , .. , Cogito, ergo sum (. — ", "). , . Swift .
追加資料
- Generic Enumeration: Optional (developer.apple.com)
- Instance Property: unsafelyUnwrapped (developer.apple.com)
- Swift Language Guide: Optionals (developer.apple.com)
- Unowned References and Implicitly Unwrapped Optional Properties (developer.apple.com)
- Nil-Coalescing Operator (developer.apple.com)
- Optional Chaining (developer.apple.com)
- Optional Protocol Requirements (developer.apple.com)
- The as! Operator (developer.apple.com)
- Converting Errors to Optional Values (developer.apple.com)
- Nullability and Objective-C (developer.apple.com)
- Nullability and Optionals (developer.apple.com)
- Xcode 6.3 Release Notes: Objective-C Language Enhancements (developer.apple.com)
- Option type (en.wikipedia.org)
- Nullable type (en.wikipedia.org)
- Re-implementing Optionals using Swift's powerful enum type (jamesonquave.com)
UPD: (by Alexander Zimin) init(nilLiteral: ())
:
var test = Optional<Int>(nilLiteral: ())
, Apple .