基本(The Basics)
最終更新日: 2023/12/29
原文: https://docs.swift.org/swift-book/LanguageGuide/TheBasics.html
一般的な種類のデータを操作し、基本的な構文を記述する。
Swift は、整数のための Int
、浮動小数点の値のための Double
、真偽値のための Bool
、文字列のための String
を含む、多くの基本的なデータ型を提供しています。
また、3 つのより強力な collection 型(Array
, Set
, Dictionary
)も提供しています。詳細は Collection Types(コレクション型)に記載しています。
Swift は、名前を特定して、値を保持したり、その値を参照するために変数を使います。また、変数の値を変更できなくすることで、より幅広い方法で変数を使用することができます。これらは、定数として知られており、Swift 内では、値を変更する必要がない場合でも、コードを安全で意図を明確にするために使われます。
これまで見たことがあるような型に加え、Swift はタプルのような、より応用的な型を導入します。タプルは値を 1 つのグループとして扱うことができます。タプルを使用すると、複数の値を 1 つの値の組み合わせとして関数から返すことができます。
Swift は値が存在しないかもしれない値を扱うオプショナル型を導入しています。オプショナルは、「値が存在していて、これは x と等しい」もしくは「値は一切存在しない」ということを表します。
Swift は型安全な言語です。つまり、言語として、コードで扱う値の型を明確にしてくれます。例えば、String
が必要な場合に、この型安全な特徴が、間違って Int
を渡してしまうことを防いでくれます。同様に、オプショナルではない String
にオプショナルの String
を誤って渡してしまうことも防いでくれます。型安全なことで、開発中に、素早くエラーの発見と修正をすることができます。
定数と変数(Constants and Variables)
定数と変数は特定の型の値(数字の 10
や文字列の "Hello"
など)と名前(maximumNumberOfLoginAttempts
や welcomeMessage
)を関連付けます。定数は一度値を設定すると変更することはできません。一方で、変数は後で異なった値を設定できます。
定数と変数の宣言(Declaring Constants and Variables)
定数と変数は、使用する前に定義されていなければなりません。定数は let
、変数は var
キーワードで定義します。ここで、ユーザが何回ログインをしようとしたか試行回数を追跡する定数と変数の例を紹介します。
let maximumNumberOfLoginAttempts = 10
var currentLoginAttempt = 0
このコードは下記のように読み取れます。
「maximumNumberOfLoginAttempts
という定数を定義して、10
という値を設定します。次に currentLoginAttempt
という変数を定義して、0
という初期値を設定します」
この例では、最大回数は変更しないので定数で定義しています。試行回数はログインを試みる度に増やさなければならないため、変数として今の試行回数を定義しています。
コードに格納された値が変化しない場合は、常に `let`` キーワードで定数として宣言してください。変数は、変化する値を格納する場合にのみ使用します。
定数や変数を宣言するとき、上の例のように宣言の一部として値を与えることができます。あるいは、プログラムの後半で初期値を代入することもできます。
var environment = "development"
let maximumNumberOfLoginAttempts: Int
// maximumNumberOfLoginAttemptsにはまだ値がない。
if environment == "development" {
maximumNumberOfLoginAttempts = 100
} else {
maximumNumberOfLoginAttempts = 10
}
// この時点でmaximumNumberOfLoginAttemptsには値が存在し、値を読み取ることができる。
この例では、ログイン試行回数の最大値は一定で、その値は環境に依存します。開発環境では 100 であり、他の環境では 10 です。if
文の両方のブランチで maximumNumberOfLoginAttempts
を特定の値で初期化しているため、定数が常に値が存在することを保証しています。この方法で初期値を設定するときに Swift がコードをチェックする方法についての情報は、定数宣言(Constant Declaration)を参照してください。
カンマ(,
)区切りで 1 行の中に複数の定数や変数を定義することもできます。
var x = 0.0, y = 0.0, z = 0.0
型注釈(Type Annotations)
定数や変数を定義するときに、保持する値の種類をより明確にするために、型注釈を使用することができます。定数や変数の名前の後にコロン(:
)を置いて、型注釈を書きましょう。
この例では welcomeMessage
という変数に型注釈を書いて String
が保持されることを示しています。
var welcomeMessage: String
定義の中のコロンは、「~型の~」を意味します。なので、このコードは下記のように読み取れます。
「String
型の welcomeMessage
という変数を定義します」
「String
型の」という句は、どんな String
型の値も保持できることを意味します。つまり、保持することができる「物事の型(種類)」だと考えましょう。
welcomeMessage
にはエラーなしに文字列を設定できます。
welcomeMessage = "Hello"
1 行の中で、同じ型の複数の変数をカンマで区切って定義することもできます。この際、型注釈は最後の変数の後に 1 つ付けます。
var red, green, blue: Double
NOTE
実際に型注釈を書く必要はあまりありません。定義時に定数や変数に初期値を与えた場合、 Swift はたいていそれらの型を推論できます(詳細はType Safety and Type Inference(型安全と型推論)。上記のwelcomeMessage
の例では、初期値を与えていないため、推論をすることができないため、welcomeMessage
変数は型注釈で型を特定しています。
定数と変数の命名(Naming Constants and Variables)
定数名と変数名には、Unicode 文字も含めた、ほとんどの文字を含めることができます。
let π = 3.14159
let 你好 = "你好世界"
let 🐶🐮 = "dogcow"
定数名と変数名に、ホワイトスペース、数学記号、矢印、公式ではない Unicode スカラ値、罫線素片、書式文字は含められません。また、数字から始めることはできません(他の位置に数字を含めることはできます)
一度ある型の定数や変数を定義すると、同じ名前で再定義することはできません。また、異なる型の値を保持することもできません。定数を変数に、変数を定数に変換することもできません。
NOTE
Swift の予約語と同じ名前の定数や変数を使いたい場合、そのキーワードをバッククォート(`
)で囲みます。可能ではありますが、予約語を名前に使用することは本当に他に選択肢がない以外は避けましょう。
既存の変数の値を他の互換性のある型の値に変更することはできます。下記の例では、friendlyWelcome
の値を "Hello!"
から "Bonjour!"
に変更しています。
var friendlyWelcome = "Hello!"
friendlyWelcome = "Bonjour!"
// friendlyWelcome は "Bonjour!"
変数とは異なり、定数の値は最初に設定した後に変更できません。試みても、コンパイル時にエラーになります。
let languageName = "Swift"
languageName = "Swift++"
// コンパイルエラー: languageName は変更できません
定数と変数の出力(Printing Constants and Variables)
print(_:separator:terminator:)
関数を使用して、定数や変数の現在の値を出力することができます。
print(friendlyWelcome)
// Bonjour!
print(_:separator:terminator:)
関数はグローバル関数で、1 つ以上の値を適切なアウトプット先に出力します。Xcode では、print(_:separator:terminator:)
関数を使用すると、 Xcode のコンソールパネルへ値を出力します。separator
と terminator
パラメータには、デフォルト値が用意されているので省略可能です。デフォルトでは最後に改行を追加します。改行を付けたくない場合は terminator
に空文字を渡してください。例えば print(someValue, terminator: "")
。詳細はDefault Parameter Values(デフォルトパラメータ値)。
Swift は長い文字列の中で定数や変数をプレースホルダとして使用したい場合、文字列補間を使い、定数や変数の現在値に置き換えるように Swift に伝えることができます。名前を括弧(()
)で囲み、開始括弧の前にバックスラッシュ(\
)を付けます。
print("The current value of friendlyWelcome is \(friendlyWelcome)")
// The current value of friendlyWelcome is Bonjour!
NOTE
文字列補間で使用できるオブションはString Interpolation(文字列補間)に記載しています
コメント(Comments)
コード内に実行されないテキスト(メモやリマインダーなど)を含めるためにはコメントを使います。コメントはコンパイル時に Swift のコンパイラからは無視されます。
Swift のコメントは C 言語のコメントにとてもよく似ています。1 行のコメントは 2 つのスラッシュ(//
)で開始します。
// これはコメントです
複数行の場合は、スラッシュ+アスタリスク(/*
)で開始し、アスタリスク+スラッシュ(*/
)で終了します。
/* これはコメントです
複数行に渡って書くこともできます */
C 言語の複数行コメントとは異なり、Swift ではコメントを他の複数行コメントにネストさせることができます。1 つ目の複数行コメントブロックの中で、2 つ目の複数行ブロックコメントを開始します。2 つ目のブロックは、1 つ目のブロックの前にコメントを閉じます。
/* これは最初の複数行コメントの開始です
/* これはネストした2番目の複数行コメントの開始です
これはネストした2番目の複数行コメントの終わりです */
これは最初の複数行コメントの終わりです */
ネストした複数行コメントを使用することで、長い複数行コメントの中でも簡単に素早くその部分だけコメントアウトすることができます。
セミコロン(Semicolons)
他の言語とは異なり、Swift ではコードの 1 つ 1 つの宣言の最後に、セミコロン(;
)を付ける必要がありません(付けることも可能ではあります)。一方で、1 行に複数のステートメントを書きたい場合は必要になります。
let cat = "🐱"; print(cat)
// 🐱
整数(Integers)
整数は小数点のない整数値を全部含みます(42
、-23
など)。整数とは符号付き(正の値、または 0、または負の値) または符号なし(正の値、または 0) です。
Swift は符号付きと符号なしの整数値を 8, 16, 32, 64 ビットの形式で提供します。これらは C 言語の名前に合わせて命名されています。8 ビットの符号なし整数は UInt8
、32 ビットの符号付き整数は Int32
です。Swift の全ての型と同様に、これらの整数値型の頭文字は大文字です。
整数の境界(Integer Bounds)
min
と max
プロパティを使用して、各整数値型の最小値と最大値にアクセスすることができます。
let minValue = UInt8.min // minValue は 0 で、 UInt8 型です
let maxValue = UInt8.max // maxValue は 255 で UInt8 型です
これらのプロパティの値は、適切なサイズの整数値型で(上記の例だと UInt8
)で、同じ型の他の値と一緒に式の中で使用することができます。
Int
多くの場合、整数値のサイズを特定する必要はありません。Swift はプラットフォームに応じて決まるサイズと同じサイズをもつ Int
という型を提供しています。
- 32 ビットのプラットフォームの場合、
Int
はInt32
と等しい - 64 ビットのプラットフォームの場合、
Int
はInt64
と等しい
特定のサイズの整数値を扱う必要がない限り常に Int
を使用するようにしてください、これはコードの一貫性と互換性を保つ手助けとなります。32 ビットのプラットフォームでは、Int
は -2,147,483,648
から 2,147,483,647
まで保持することができます。これは整数値が使われる多くの場合において、十分に大きい範囲です。
UInt
Swift は符号なしの整数値型も提供しています。これも、プラットフォームに応じて決まるサイズと同じサイズを持ちます。
- 32 ビットのプラットフォームの場合、
UInt
はUInt32
と等しい - 64 ビットのプラットフォームの場合、
UInt
はUInt64
と等しい
NOTE
UInt
は特別にプラットフォームに応じて決まるサイズと同じサイズの 符号なし整数型を扱いたい場合にのみ使用してください。そうでない場合、負の値にならないとしてもInt
を使用する方が好ましいです。一貫してInt
を整数値に使用することで、異なる整数値型間の変換を避け、合致する型をコンパイラが推論することで、互換性を保つことができます。詳細はType Safety and Type Inference(型安全と型推論)。
浮動小数点数(Floating-Point Numbers)
浮動小数点数は、小数部分を持つ数値です(3.14159
, 0.1
, -273.15
など)。
浮動小数点数値型は、整数値型よりもより広い範囲の値を表現でき、Int
より大きい(またはより小さい)値を保持できます。Swift は 2 つの符号付き浮動小数点数値型を提供しています。
Double
は 64 ビットの浮動小数点数を表しますFloat
は 32 ビットの浮動小数点数を表します
NOTE
Double
は最小で15桁の精度の小数値を持ち、Float
は6桁の精度の小数値を持ちます。適切な浮動小数点数値型は、扱いたい数値の特性と範囲によります。どちらでも良い場合は、Double
が好まれます。
型安全と型推論(Type Safety and Type Inference)
Swift は型安全な言語です。型安全な言語は、扱っている値の型を明確にします。String
が必要な場合に、間違って Int
を渡すようなことはありません。
型安全なことから、コンパイル時に型チェックが行われ、間違った型に対してはエラーを示します。これによって開発時に素早くエラーに気がつき修正をすることができます。
型チェックによって、異なる型の値を扱う場合に間違った型を代入してしまうエラーを回避することができます。しかし、全ての定数と変数の定義時に型を特定しなければならないということではありません。特定しない場合、Swift は適切な型への推論を行います。コンパイラがコンパイル時に与えられた値を調べることで、式の型を自動で推論してくれます。
この型推論のおかげで、Swift は C 言語や Objective-C のような言語と比べて、型を宣言する必要はあまりありません。定数と変数は明示的に型を記載する場合もありますが、型を特定する多くの作業は Swift の側で行なってくれます。
型推論は、特に初期値を伴って変数や定数を宣言するときに役に立ちます。特に、リテラル値(または literal)な値を代入する際によく機能します。(リテラルな値とは、下記の例にあるようなコードに直接出てくる 42, 3.14159
のような値のことです)
例えば、42
を新しい定数に型を記載せずに代入すると、整数値型に見える数値リテラルで初期化していることから、Swift はその型を推論して Int
と判断します。
let meaningOfLife = 42
// meaningOfLife は Int 型と推論されます
同様に、浮動小数点数を型なしで宣言した場合、Swift は Double
を定義したと推論します。
let pi = 3.14159
// pi は Double 型と推論されます
Swift は浮動小数点数を推論する際に、常に(Float
よりも)Double
を選択します。
式の中で整数値と浮動小数点数を組み合わせた場合は、コンテキストから多くの場合 Double
と推論されます。
let anotherPi = 3 + 0.14159
// anotherPi は Double 型と推論されます
3
には明確な型がありませんが、後ろの浮動小数点数の値から Double
と推論されます。
数値リテラル(Numeric Literals)
数値リテラルは、下記の方法で書くことができます。
- プレフィックスなしの 10 進数
0b
をプレフィックスにした 2 進数0o
をプレフィックスにした 8 進数0x
をプレフィックスにした 16 進数
これらの数値リテラルは、17
という 10 進数を下記のように保持します。
let decimalInteger = 17
let binaryInteger = 0b10001 // 17 は2進数表記
let octalInteger = 0o21 // 17 は8進数表記
let hexadecimalInteger = 0x11 // 17 は16進数表記
浮動小数点数リテラルは、10 進数と 16 進数にできます。小数点の左と右の両方に必ず整数値が必要です。10 進数の浮動小数点数は、指数を持つ場合があります(小文字または大文字の e
)。16 進数の浮動小数点数は必ず指数を指定しなければなりません(小文字または大文字の p
)。
exp
の指数を持つ 10 進数では、元の値にを掛けます。
1.25e2
は 1.25 x または125.0
と等しい1.25e-2
は 1.25 x または0.0125
と等しい
exp
の指数を持つ 16 進数では、元の値にを掛けます。
0xFp2
は 15 x または60.0
と等しい0xFp-2
は 15 x または3.75
と等しい
下記の浮動小数点数は、全て 12.1875
という 10 進数を表しています。
let decimalDouble = 12.1875
let exponentDouble = 1.21875e1
let hexadecimalDouble = 0xC.3p0
整数値リテラルは、読みやすくするために追加でフォーマットを含めることができます。整数値や浮動小数点数へ追加の 0 を加えたり、アンダースコア(_
)を追加することで可読性を向上させることができます。このフォーマットに整数値自体への影響はありません。
let paddedDouble = 000123.456
let oneMillion = 1_000_000
let justOverOneMillion = 1_000_000.000_000_1
数値型の変換(Numeric Type Conversion)
通常整数値を扱う際は、負の値にならないことがわかっていたとしても定数や変数に Int
型を使いましょう。全ての状況でデフォルトに Int
を使用することで、整数値の定数と変数を特別な手順なしに互換可能にし、数値リテラルから推論される型とも合致します。
特別な理由ある場合のみ、他の整数値型を使いましょう。例えば、外部リソースから特定サイズの型が指定されている場合やパフォーマンス、メモリの使用量や他の最適化が必要な場合など、がこれに当たります。明示的にサイズを指定することで思わぬオーバーフローを起こしたり、暗黙的にそのデータの特性を表現することができます。
整数の変換(Integer Conversion)
整数値の定数や変数に保持できる範囲は、それぞれの整数値型によって異なります。Int8
の定数や変数は、−128
から 127
まで保持できます。UInt8
の定数や変数は、0
から 255
まで保持できます。指定したサイズの整数値型の定数や変数に合わない値を設定しようとするとエラーになります。
let cannotBeNegative: UInt8 = -1
// UInt8 はマイナス値を保持できません。そのためエラーになります
let tooBig: Int8 = Int8.max + 1
// Int8 は最大値以上のマイナス値を保持できません。
// そのためこれもエラーになります
このように、整数値型によって保持できる範囲が異なるため、ケースごとに型の変換をしなければなりません。この明示的に指定する方法によって、隠れた変換ミスを防ぎ、コードで型変換が起こっていることを明確に表現することができます。
ある特定の整数値型を他の型に変換するためには、既存の整数値から変換したい型の新しい値を生成する必要があります。下記の例では、定数 twoThousand
は UInt16
ですが、定数の one
は UInt8
です。この 2 つは同じ型ではないので直接足し算をすることができません。そこで、この例では、one
を使用して UInt16
の新しい値を作るために UInt16(one)
を呼び、元の値に置き換えて使います:
let twoThousand: UInt16 = 2_000
let one: UInt8 = 1
let twoThousandAndOne = twoThousand + UInt16(one)
今はどちらも UInt16
なので、足し算は可能です。そして計算結果の定数(twoThousandAndOne
)も UInt16
に推論されます。
SomeType(ofInitialValue)
という形式は、初期値を渡して初期化を行う Swift のデフォルトの方法です。裏側では、UInt16
型が UInt8
の値を受け取って、新しい UInt16
型の値を生成しています。とはいっても、あらゆる型を渡せるわけではありません。UInt16
型が提供するイニシャライザに合った型が必要です。新しい型を渡して初期化する方法は、Extensions(拡張)に記載しています。
整数と浮動小数点数の変換(Integer and Floating-Point Conversion)
整数値と浮動小数点数の変換は明示的に行わなければなりません:
let three = 3
let pointOneFourOneFiveNine = 0.14159
let pi = Double(three) + pointOneFourOneFiveNine
// pi は 3.14159 で Double 型と推論されます
ここに、定数 3
の値が Double
型の新しい値を作って使われています。そのため、両方の型が同じになり、足し算が可能です。変換をしない場合、足し算はできません。
整数値から浮動小数点数への変換は明示的に行わなければなりません。整数値は Double
や Float
で初期化できます。
let integerPi = Int(pi)
// integerPi は 3 で Int 型と推論されます
浮動小数点数の値は新しい整数値として初期化される場合、必ず小数点は切り捨てられます。つまり、4.75
は 4
、-3.9
は -3
になります。
NOTE
整数値の定数と変数を組み合わせるルールは、数値リテラルのルールとは異なります。リテラル値の3
はリテラル値の0.14159
とそのまま加算できます。リテラル値は明示的な型を有していないため、コンパイラがその値を評価するときに型を推論します。
タイプエイリアス(Type Aliases)
タイプエイリアス(Type Aliases)は既存の型に別の名前を定義します。typealias
キーワードを使います。
タイプエイリアスは、既存の型をコンテキストに沿ったより適切な名前で参照したい場合に有効です。例えば、外部リソースの特定のサイズのデータを扱いたい場合など:
typealias AudioSample = UInt16
定義したタイプエイリアスは、元の名前で使用していた所にも使用することができます:
var maxAmplitudeFound = AudioSample.min
// maxAmplitudeFound は 0
ここでは、AudioSample
が UInt16
のタイプエイリアスとして定義されています。エイリアスなので、AudioSample.min
の実態は UInt16.min
で、maxAmplitudeFound
の初期値は 0
になります。
ブール値(Booleans)
Swift は、Bool
と呼ばれる基本的なブール値を持っています。ブール値は、真(true)か偽(false)のみを値として取ることから、論理値(logical)として参照されます。Swift では true
と false
の 2 つのブールの定数を提供しています。
let orangesAreOrange = true
let turnipsAreDelicious = false
orangesAreOrange
と turnipsAreDelicious
は、ブール値リテラルで初期化されていることから Bool
と推論されます。これまで見てきた Int
や Double
のように、true
や false
を値に設定すれば、明示的に Bool
と指定する必要はありません。既に型がわかっている他の定数や変数を使用して初期化する際に、Swift の型推論を活用することで、より簡潔で読みやすいコードを書くことができます。
ブール値は、条件文を扱う際にとても有効です。例えば、if
文など:
if turnipsAreDelicious {
print("Mmm, tasty turnips!")
} else {
print("Eww, turnips are horrible.")
}
// Eww, turnips are horrible.
if
のような条件文については、Control Flow(制御フロー)でより詳細に記載しています。
Swift は型安全なので、Bool
以外の値を Bool
として使うことはできません。次の例はコンパイルエラーになります:
let i = 1
if i {
// この例はコンパイルされず、エラーになります
}
一方で、次の例は問題ありません:
let i = 1
if i == 1 {
// コンパイルできます
}
i == 1
の比較結果は Bool
なので、型チェックを通過できます。i == 1
のような比較については、Basic Operators(基本演算子)でより詳しく記載しています。
他の型安全性を示した例と同様に、型の違いによる予期せぬエラーの発生を防ぎ、型が明確なので、コードの意図をより明確に表すことができます。
タプル(Tuples)
タプル(Tuples)は、複数の値を 1 つのまとまりにグループ化します。タプル内の値にはどんな型も入れることができ、全ての型を同じにする必要はありません。
下記の例の (404, "Not Found")
は、 HTTP ステータスコードを表したタプルです。HTTP ステータスコードは、Web ページを取得するリクエストを送ると Web サーバから返ってくる特別な値です。404 Not Found
のステータスコードは、リクエストした Web ページが存在しなかった場合に返ってきます。
let http404Error = (404, "Not Found")
// http404Error は (Int, String)型で (404, "Not Found")と等しい
(404, "Not Found")
タプルは、HTTP ステータスコードを 2 つの値:(数値と人が理解できる説明文)に分けた Int
と String
を 1 つのグループにまとめています。これは「(Int, String)
型のタプル」と説明できます。
タプルは、任意の順序で、異なる任意の型を組み合わせることができます。例えば、(Int, Int, Int)
や (String, Bool)
型のタプルも作ることができますし、必要に応じて順番の入れ替えも可能です。
タプルの個々の内容をそれぞれ定数や変数に分けて扱うこともでき、他の値と同じようにアクセスすることができます:
let (statusCode, statusMessage) = http404Error
print("The status code is \(statusCode)")
// The status code is 404
print("The status message is \(statusMessage)")
// The status message is Not Found
もしタプルの一部だけが必要な場合、タプルを展開するときに、アンダースコア(_
)を使用して無視することができます。
let (justTheStatusCode, _) = http404Error
print("The status code is \(justTheStatusCode)")
// The status code is 404
各値へのアクセス方法としては、0 から始まるインデックスを使用することもできます:
print("The status code is \(http404Error.0)")
// The status code is 404
print("The status message is \(http404Error.1)")
// The status message is Not Found
タプルの定義時に、名前を付けることもできます:
let http200Status = (statusCode: 200, description: "OK")
名前を付けた場合、その名前を使用して各値へアクセスすることができます:
print("The status code is \(http200Status.statusCode)")
// The status code is 200
print("The status message is \(http200Status.description)")
// The status message is OK
タプルは、特に関数の戻り値で有効に活用できます。Web ページを取得する関数は、取得の成否の結果を (Int, String)
で返すかもしれません。2 つの異なる型の値を持ったタプルを返すことで、1 つの型の 1 つの値を返すよりも、関数はより有益な情報を提供できます。より詳しくは、Functions with Multiple Return Values(複数の戻り値がある関数)を参照ください。
オプショナル(Optionals)
オプショナルは、値が存在しないかもしれないときに使用します。オプショナルは 2 つの可能性を表します: 指定された型の値が存在してアンラップすることで値にアクセスすることができる、もしくは、値が全く存在しない
欠如している可能性のある値の例として、 Swift の Int
型には String
を Int
へと変換するイニシャライザがあります。しかし、文字列の中で Int
に変換できるものは一部だけです。「123
」という文字列は数値 123
に変換できますが、「hello world
」は変換することができません。
下記の例は String
を Int
へ変換するイニシャライザの例です。
let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
// convertedNumber の型は 「Optional<Int>」 です。
上記コードのイニシャライザは失敗するかもしれないので、Int
ではなく、オプショナルの Int
を返します。
オプショナル型を記述するには、オプショナルが含む型の名前の後に疑問符( ?
) を書きます。例えば、オプショナルな Int
の型は Int?
です。オプショナルな Int
は常にある Int
の値を含むか、 nil
かのどちらかです。 それ以外の値、例えば Bool
や String
を含めることはできません。
nil
オプショナルな変数は、特別な値 nil
を代入することで、値のない状態を設定することができます:
var serverResponseCode: Int? = 404
// serverResponseCode Int の値の 404 を含んでいます
serverResponseCode = nil
// serverResponseCode は 値を含んでいません
もし、デフォルトで値を与えずにオプショナルな変数を定義した場合、その変数には自動で nil
が設定されます:
var surveyAnswer: String?
// surveyAnswer には自動で nil が設定されます
if
文を使用してオプショナル値を nil
と比較することで、値を含んでいるかどうかのチェックができます。この比較は、等しい(==
)または等しくない(!=
)演算子を使用して行います。
オプショナル値が値を含んでいる場合、nil
と「等しくない」と見なされます。
let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
if convertedNumber != nil {
print("convertedNumber contains some integer value.")
}
// convertedNumber contains some integer value.
非オプショナルの定数や変数に nil
を使用することはできません。コード内の定数や変数が特定の条件下で値の欠如を扱う必要がある場合、適切な型のオプショナル値として宣言します。非オプショナル値として宣言された定数や変数は、 nil
を含むことが絶対にできないことが保証されています。非オプショナル値に nil
を割り当てようとすると、コンパイル時にエラーが発生します。
このオプショナル値と非オプショナル値を区別することで、どの情報が欠如しているか明示的にマークすることができ、欠如している値を取り扱うコードを書くのが簡単になります。オプショナル値を非オプショナル値として誤って扱うことはできません。なぜなら、このようなミスはコンパイル時にエラーを生成するからです。値をアンラップした後、その値を利用する他のコードは nil
のチェックをする必要がなくなるため、コードの異なる部分で同じ値を何度もチェックする必要がありません。
オプショナル値にアクセスするとき、コードは常に nil
の場合と非 nil
の場合の両方を処理します。値が欠如しているときにできるいくつかのことがあり、次のセクションで説明されています。
nil
のときに値を操作するコードをスキップするnil
の値を伝播させるには、nil
を返すか?
を使用します。?
演算子については、 Optional Chainig (オプショナルチェーン) を参照してください??
演算子を使用して、フォールバック値を提供します!
演算子を使用して、プログラムの実行を停止します
NOTE Objective-C では、
nil
は存在しないオブジェクトへのポインタです。Swiftでは、nil
はポインタではありません。特定の型の値の欠如です。任意の型のオプショナル値は、オブジェクトタイプだけでなくnil
を設定することができます。
オプショナルバインディング(Optional Binding)
オプショナル値に、オプショナルバインディングを使用して、値を含んでいるかどうかを判定できます。もし含んでいる場合は、一時的な定数や変数として値を使用できるようになります。オプショナルバインディングは、if
や guard
、 while
文の 1 つのアクションで、オプショナル値に値が存在することを証明し、定数や変数にその内部の値を設定することを、まとめて行うことができます。if
や guard
、 while
の詳細はControl Flow(制御フロー)を参照ください。
if
文でオプショナルバインディングを行う場合、次のように書きます:
if let constantName = someOptional {
statements
}
Optionals(オプショナル)の中の例で出てきた possibleNumber
は、強制アンラップの代わりに、オプショナルバインディングを使用して書き換えることができます。
if let actualNumber = Int(possibleNumber) {
print("The string \(possibleNumber) has an integer value of \(actualNumber)")
} else {
print("The string \(possibleNumber) couldn't be converted to an integer")
}
// The string 123 has an integer value of 123
このコードはこのような意味に読み取れます。
「Int(possibleNumber)
が返すオプショナルの Int
が値を含んでいた場合、actualNumber
にその値を設定します」
この変換が成功した場合、actualNumber
定数は if
文の最初の分岐内で使用することができます。オプショナル内にラップされている値で既に初期化は完了しているので、対応する非オプショナルの型を持っています。この場合、 possibleNumber
の型は Int?
なので、 actualNumber
の型は Int
です。
オプショナルに含まれている値にアクセスした後、元のオプショナルの定数または変数を参照する必要がない場合は、新しい定数または変数に同じ名前を使用できます。
let myNumber = Int(possibleNumber)
// ここでmyNumberはオプショナルのInt
if let myNumber = myNumber {
// ここでmyNumberはオプショナルではないInt
print("My number is \(myNumber)")
}
// My number is 123
このコードは、前の例のコードと同様に、myNumber
に値が含まれているかどうかを確認することから始まります。myNumber
に値がある場合、myNumber
という名前の新しい定数の値がその値に設定されます。if
文の本文内では、myNumber
は新しい非オプショナルの定数が参照されます。if
文の前後で myNumber
を使うと、元のオプショナルの定数 Int
が参照されます。
この種のコードはとても一般的で、短いスペルを使用してオプショナル値をアンラップできます。アンラップする定数または変数の名前だけを記述します。ラップされていない新しい定数または変数は、オプショナル値と同じ名前を暗黙的に使用します。
if let myNumber {
print("My number is \(myNumber)")
}
// My number is 123
オプショナルバインディングは定数と変数の両方に使用することができます。if
文の最初の分岐内で actualNumber
を変更したい場合は、if var actualNumber
と書くことで、定数の代わりに変数としてこのオプショナル値を使用できます。
1 つの if
文の中に、複数のオプショナルバインディングとブール値をカンマ(,
)区切りで含めることができます。そのうちのいずれかが nil
または false
の場合、if
文全体が false
と判断されます。次の if
文はこれに該当します。
if let firstNumber = Int("4"), let secondNumber = Int("42"), firstNumber < secondNumber && secondNumber < 100 {
print("\(firstNumber) < \(secondNumber) < 100")
}
// 4 < 42 < 100
if let firstNumber = Int("4") {
if let secondNumber = Int("42") {
if firstNumber < secondNumber && secondNumber < 100 {
print("\(firstNumber) < \(secondNumber) < 100")
}
}
}
// 4 < 42 < 100
if
文の中でオプショナルバインディングによって作られた定数や変数は、if
文の中でしか使えません。もし他でも使用したい場合は、guard
文を使用することで、guard
文の次から使用することができます。詳細はEarly Exit(早期リターン)に記載しています。
フォールバック値の提供(Providing a Fallback Value)
欠如している値を処理する別の方法は、nil
結合演算子( ??
) を使用してデフォルトの値を提供することです。 ??
の左側のオプショナル値が nil
でなければ、その値はアンラップされて使用されます。それ以外の場合、 ??
の右側の値が使用されます。例えば、以下のコードは、名前が指定されていればその名前であいさつし、名前が nil
の場合は汎用的な挨拶を使用します。
let name: String? = nil
let greeting = "Hello, " + (name ?? "friend") + "!"
print(greeting)
// Hello, friend!
??
を使用してフォールバック値を提供する方法の詳細については、Nil-Coalescing-Operator( nil
合体演算子)を参照してください。
強制アンラップ(Force Unwrapping)
nil
がプログラマのエラーや破損した状態などの回復不可能な失敗を表す場合、オプショナル値の名前の末尾に感嘆符( !
)を追加することで、その基本の値にアクセスすることができます。
これはオプショナル値を 強制的にアンラップする として知られています。非 nil
の値を強制的にアンラップすると、結果はアンラップされた値となります。 nil
の値を強制的にアンラップすると、ランタイムエラーが発生します。 !
は、実質的に fatalError(_:file:line:)
) の短縮形です。以下のコードは、 2 つの同等のアプローチを示しています。
let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
let number = convertedNumber!
guard let number = convertedNumber else {
fatalError("The number was invalid")
}
上記のコードの両方のバージョンは、convertedNumber
が常に値を持っていることに依存しています。その要件をコードの一部として書くことで、上記のいずれかのアプローチを使用して、実行時にその要件が真であることをコードがチェックできます。
実行時にデータ要件を強制し、前提を確認する詳細については、Assertions and Preconditions(アサーションと事前条件)を参照してください。
暗黙アンラップオプショナル(Implicitly Unwrapped Optionals)
上記で書いているように、オプショナルは定数や変数に「値が存在しない」可能性があることを示します。オプショナルは値が存在するかどうかを if
文の中でチェックでき、存在している場合は、オプショナル内の値にアクセスするために、オプショナルバインディングを使用してアンラップすることができます。
時々、オプショナルに一度値が設定された後は必ず値が存在するということが明らかなこともあります。このような場合、常に値があることはわかっているので、アクセスする度にオプショナル値のチェックとアンラップすることを省略できれば便利です。
このようなオプショナルは、暗黙アンラップオプショナルとして定義されています。?
の代わりに !
を型の後に付けることで、暗黙アンラップオプショナルを書くことができます(String?
の代わりに String!
と書くなど)。コード内で使用するオプショナル値の後に !
付けるよりも、定義した型の後に !
を付けます。
暗黙アンラップオプショナルは、オプショナル値の定義後すぐに値が設定され、それ以降はずっと値が存在していることが確実な場合に役に立ちます。Swift での暗黙アンラップオプショナルの主な使われ方としては、クラスの初期化時があります。Unowned References and Implicitly Unwrapped Optional Properties(非所有参照と暗黙アンラップしたオプショナルプロパティ)に記載しています。
後で変数が nil
になる可能性がある場合、暗黙アンラップオプショナル値を使用しないでください。変数の有効期間中に nil
をチェックする必要がある場合は、常に通常のオプショナル型を使用してください。
暗黙アンラップオプショナルは、内部的には通常のオプショナルですが、非オプショナルのように使用することもできます。次の例は、オプショナルと暗黙アンラップオプショナルで、明示的に String
を型として記載している値へアクセスするときの動きの違いを表しています。
let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // ! が必要
let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString // ! は自動的に不要になる
暗黙アンラップオプショナル値を必要なときにオプショナル値へ強制アンラップできるようにしていると見なすことができます。暗黙アンラップオプショナル値を使用するとき、Swift は強制アンラップします。上記のコードでは、オプショナル値の assumedString
は、implicitString
が明示的に非オプショナルな String
を宣言しているため、代入される前に強制アンラップされています。下記のコードでは、optionalString
は明示的に型を宣言していないため、通常はオプショナル値になります。
let optionalString = assumedString
// optionalString は String? 型で assumedString は 強制アンラップする必要はありません
暗黙アンラップオプショナル値が nil
の場合に内部の値にアクセスしようとすると、実行時エラーが発生します。これは !
を付けた通常のオプショナル値で値が存在しない場合に強制的にアンラップしたときの動きと同じです。
暗黙アンラップオプショナル値が nil
かどうかのチェックは通常のオプショナル値と同じ方法でできます。
if assumedString != nil {
print(assumedString!)
}
// An implicitly unwrapped optional string.
暗黙アンラップオプショナルはオプショナルバインディングもできます。1 つの文の中で、チェックとアンラップができます。
if let definiteString = assumedString {
print(definiteString)
}
// An implicitly unwrapped optional string.
エラーハンドリング(Error Handling)
実行中のエラーに対応するためには、エラーハンドリングを行います。
関数の成功や失敗を伝えるために、値の有無を利用するオプショナルと異なり、エラーハンドリングは裏側の失敗の原因を特定でき、必要ならばエラーをプログラムの他の箇所へ伝播させることができます。
関数がエラーに遭遇すると、エラーをスローします。そして、この関数の呼び出し元でエラーをキャッチして、適切に応答することができます。
func canThrowAnError() throws {
// この関数はエラーをスローするかもしれません
}
エラーは、定義に throws
キーワードを含めることで、エラーをスローすることを示せます。エラーをスローする関数を呼ぶ場合、式の前に try
キーワードを付けます。
Swift は catch
でエラーがキャッチされるまで、現在のスコープを抜けてエラーを自動で伝播します。
do {
try canThrowAnError()
// エラーはスローされませんでした
} catch {
// エラーがスローされました
}
do
は新しいスコープを生成して、エラーを 1 つ以上の catch
句でキャッチすることができます。
これは複数のエラーに応答するためにエラーハンドリングを使った例です:
func makeASandwich() throws {
// ...
}
do {
try makeASandwich()
eatASandwich()
} catch SandwichError.outOfCleanDishes {
washDishes()
} catch SandwichError.missingIngredients(let ingredients) {
buyGroceries(ingredients)
}
この例では、makeASandwich()
関数は、綺麗な皿を使えない場合や材料が足りない場合、エラーをスローします。makeASandwich()
はエラーをスローする可能性があるため、この関数の呼び出しは try
式で包まれています。do
文で関数の呼び出しを包み、スローされたエラーは catch
句でキャッチされます。
エラーがスローされない場合、eatASandwich()
関数が呼ばれます。エラーがスローされ、それが SandwichError.outOfCleanDishes
case と合致する場合、washDishes()
関数が呼ばれます。SandwichError.missingIngredients
ケースに合致する場合、buyGroceries(_:)
関数が catch
でキャッチされた [String]
値をパラメータに呼び出されます。
エラーのスロー、キャッチそして伝播は、Error Handling(エラーハンドリング)でより詳細に書かれています。
アサーションと事前条件(Assertions and Preconditions)
アサーションと事前条件は実行時のチェックです。他のコードが実行される前に必要不可欠な条件が満たされているかどうかを確かめることができます。アサーションや事前条件のブール値が true
と評価される場合、コードは通常通り継続します。false
の場合、現在のプログラムの状態は不正となり、コードの実行は中断し、アプリは終了します。
アサーションと事前条件はコード上での前提となる条件や期待値を表すために使います。アサーションは開発中の間違いや間違った想定を見つけやすくし、事前条件は開発中の問題を検知しやすくします。
実行時の期待値を確認することに加えて、アサーションと事前条件はコード内のドキュメントとしても有用です。Error Handling(エラーハンドリング)で記載したエラー条件とは異なり、アサーションと事前条件は復帰可能ではなく、期待されたエラーをキャッチする手段として使用することはできません。アサーションや事前条件の失敗は、不正なプログラムの状態を表し、失敗したアサーションをキャッチする方法はありません。無効な状態からの回復は不可能です。アサーションが失敗すると、プログラムのデータの少なくとも 1 つが無効となります。しかし、それがなぜ無効であるか、また、追加の状態も無効であるのかどうかはわかりません。
アサーションと事前条件を使用することは、不正な条件を起こさないためのツールとしてコードをデザインするための代用品にはなりません。妥当なデータや状態を強制することで、不正な状態が起きた場合に、予測しやすい状態でアプリ終了させたり、プログラムをデバッグしやすくします。予測がチェックされない場合、他の場所のコードが失敗し始めるまで、この種の問題には気づかないかもしれません。そして、その間にユーザデータが破損している可能性があります。不正な状態が起きた際にすぐに実行を止めることで、不正な状態が与えるダメージを抑えることができます。
アサーションと事前条件の違いは、チェックのタイミングにあります: アサーションはデバッグビルド時にしかチェックをせず、事前条件はデバッグとプロダクションの両方のビルドでチェックされます。プロダクションビルドでは、アサーションは評価されません。つまり、開発時にはアサーションを多用してもプロダクションのパフォーマンスに影響はありません。
アサーションを使ったデバッグ(Debugging with Assertions)
Swift の標準ライブラリの assert(::file:line:)関数を呼ぶことでアサーションを書くことができます。true
か false
と評価される式と、false
だった場合に出力するメッセージを式として渡すことができます。例えば:
let age = -3
assert(age >= 0, "A person's age can't be less than zero.")
// Thisアサーションfails because -3 isn't >= 0.
この例では、age >= 0
、つまり負の値ではない場合、が true
でコードは継続して実行されます。負の値の場合、age >= 0
は false
となり、アサーションは失敗しアプリは終了します。
アサーションのメッセージは省略することができます。例えば、ただ条件を繰り返している場合などです。
assert(age >= 0)
既にチェック済みの条件に対してアサーションを呼び出したい場合、assertionFailure(_:file:line:)関数を使用して、アサーションが失敗したことを示すことができます。
if age > 10 {
print("You can ride the roller-coaster or the ferris wheel.")
} else if age >= 0 {
print("You can ride the ferris wheel.")
} else {
assertionFailure("A person's age can't be less than zero.")
}
事前条件を強制する(Enforcing Preconditions)
false
になる可能性があるものの、コードの実行を継続するためには必ず true
にならなければならない条件に対しては、事前条件を使いましょう。例えば、サブスクリプトが範囲超えエラーを起こしていないかの確認や、適切な値を関数に渡しているかなどには、事前条件を使いましょう。
事前条件はprecondition(::file:line:)関数を呼ぶことで、事前条件を書けます。true
か false
と評価される式と、false
だった場合に出力するメッセージを式として渡すことができます。例えば:
// サブスクリプトの実装内で
precondition(index > 0, "Index must be greater than zero.")
事前条件が失敗したことを示すために、preconditionFailure(_:file:line:)関数を使用することもできます。例えば、switch 文の中で、本来ならば他のケースで全ての妥当な入力値をカバーできるはずなのに、default のケースに入ってしまうケースなどがあります。
NOTE もし(
-Ounchecked
)モードでコンパイルした場合、事前条件はチェックされません。コンパイラは事前条件を常にtrue
とみなしてコードの最適化を行います。一方で、fatalError(_:file:line:)
関数は最適化の設定をしても、常に実行を中断します。fatalError(_:file:line:)
関数は、試作段階や開発の初期段階で、まだ未実装であることを示すためのスタブとして使用することができます(fatalError("Unimplemented")
と書くなど)。fatal error はコードの最適化がされないため、アサーションと事前条件とは異なり、もしこのスタブメソッドに遭遇した場合は、確実に実行を中断させることができます