基本
Swiftは、整数用のInt、浮動小数点値用のDouble、ブール値用のBool、テキスト用のStringなど、多くの基本的なデータ型を提供します。また、Swiftは3つの主要なコレクション型であるArray、Set、Dictionaryの強力なバージョンも提供しています。詳細はコレクション型で説明します。
Swiftは変数を使用して、識別名によって値を保存し参照します。また、Swiftは値を変更できない変数も広く使用します。これらは定数と呼ばれ、変更する必要のない値を扱う際にコードをより安全で意図が明確になるようにするために、Swift全体で使用されます。
基本的な型に加えて、Swiftはタプルなどの高度な型を導入しています。タプルを使用すると、値のグループを作成して受け渡すことができます。タプルを使用して、関数から複数の値を単一の複合値として返すことができます。
Swiftはオプショナル型を使用して値が存在しない場合を処理します。オプショナルは「値がある、それはxである」または「値がまったくない」のいずれかを表します。オプショナルにより、コードは値を使用する前に常に値が欠落していないかどうかを確認し、非オプショナル値は決して欠落しないことが保証されます。
Swiftは安全な言語であり、開発プロセスのできるだけ早い段階でいくつかのカテゴリのバグを見つけて修正しやすくし、特定の種類のバグが発生しないことを保証できます。型安全性により、コードが扱う値の型を明確にすることができます。コードの一部がStringを必要とする場合、型安全性により誤ってIntを渡すことを防ぎます。メモリ安全性により、初期化されていないメモリや解放されたオブジェクトではなく、有効なデータのみを扱うことが保証され、複数のコードが同時に実行されるプログラムでも安全な方法でデータを扱うことが保証されます。Swiftはコードをビルドする際にほとんどの安全性チェックを実行し、場合によってはコードの実行中に追加のチェックを実行します。
定数と変数
定数と変数は、名前(maximumNumberOfLoginAttemptsやwelcomeMessageなど)を特定の型の値(数値の10や文字列の"Hello"など)に関連付けます。定数の値は一度設定すると変更できませんが、変数は将来別の値に設定できます。
定数と変数の宣言
定数と変数は使用する前に宣言する必要があります。定数はletキーワードで、変数はvarキーワードで宣言します。以下は、ユーザーのログイン試行回数を追跡するために定数と変数をどのように使用できるかの例です。
let maximumNumberOfLoginAttempts = 10
var currentLoginAttempt = 0
このコードは次のように読むことができます。
「maximumNumberOfLoginAttemptsという新しい定数を宣言し、10の値を与えます。次に、currentLoginAttemptという新しい変数を宣言し、初期値0を与えます。」
この例では、最大値が決して変わらないため、許可されるログイン試行回数の最大値は定数として宣言されています。現在のログイン試行カウンターは、ログイン試行が失敗するたびにインクリメントする必要があるため、変数として宣言されています。
コード内の保存された値が変更されない場合は、常にletキーワードを使用して定数として宣言してください。変数は、変更される値を保存する場合にのみ使用してください。
定数または変数を宣言する際に、上記の例のように、その宣言の一部として値を与えることができます。また、最初に読み取る前に値を持つことが保証されている限り、プログラムの後で初期値を割り当てることもできます。
var environment = "development"
let maximumNumberOfLoginAttempts: Int
// maximumNumberOfLoginAttempts has no value yet.
if environment == "development" {
maximumNumberOfLoginAttempts = 100
} else {
maximumNumberOfLoginAttempts = 10
}
// Now maximumNumberOfLoginAttempts has a value, and can be read.
この例では、ログイン試行回数の最大値は定数であり、その値は環境に依存します。開発環境では100の値を持ち、それ以外の環境では10の値を持ちます。if文の両方の分岐でmaximumNumberOfLoginAttemptsが何らかの値で初期化されるため、定数は常に値を取得することが保証されます。この方法で初期値を設定する際にSwiftがコードをどのようにチェックするかについては、定数宣言を参照してください。
カンマで区切って、1行で複数の定数または複数の変数を宣言できます。
var x = 0.0, y = 0.0, z = 0.0
型アノテーション
定数または変数を宣言する際に型アノテーションを提供して、定数または変数が保存できる値の種類を明確にすることができます。型アノテーションは、定数または変数名の後にコロンを置き、その後にスペースを置き、使用する型の名前を続けて記述します。
この例では、welcomeMessageという変数の型アノテーションを提供し、変数がString値を保存できることを示しています。
var welcomeMessage: String
宣言内のコロンは「…の型は…」を意味するため、上記のコードは次のように読むことができます。
「String型のwelcomeMessageという変数を宣言します。」
「String型」というフレーズは「任意のString値を保存できる」ことを意味します。保存できる「もののタイプ」(または「ものの種類」)を意味すると考えてください。
welcomeMessage変数は、エラーなしで任意の文字列値に設定できるようになりました。
welcomeMessage = "Hello"
同じ型の関連する複数の変数を、カンマで区切り、最後の変数名の後に単一の型アノテーションを付けて、1行で定義できます。
var red, green, blue: Double
注意 実際には型アノテーションを記述する必要はほとんどありません。定義時に定数または変数の初期値を提供すると、型安全性と型推論で説明されているように、Swiftはほぼ常にその定数または変数に使用する型を推論できます。上記の
welcomeMessageの例では、初期値が提供されていないため、welcomeMessage変数の型は初期値から推論されるのではなく、型アノテーションで指定されています。
定数と変数の命名
定数と変数の名前には、Unicode文字を含むほぼすべての文字を含めることができます。
let π = 3.14159
let 你好 = "你好世界"
let 🐶🐮 = "dogcow"
定数と変数の名前には、空白文字、数学記号、矢印、私用Unicodeスカラー値、または罫線文字を含めることはできません。また、数字で始めることはできませんが、名前の他の場所には数字を含めることができます。
特定の型の定数または変数を一度宣言すると、同じ名前で再度宣言したり、異なる型の値を保存するように変更したりすることはできません。また、定数を変数に、変数を定数に変更することもできません。
注意 定数または変数にSwiftの予約キーワードと同じ名前を付ける必要がある場合は、名前として使用する際にキーワードをバッククォート(
`)で囲んでください。ただし、絶対に他に選択肢がない場合を除き、キーワードを名前として使用することは避けてください。
既存の変数の値を、互換性のある型の別の値に変更できます。この例では、friendlyWelcomeの値が"Hello!"から"Bonjour!"に変更されています。
var friendlyWelcome = "Hello!"
friendlyWelcome = "Bonjour!"
// friendlyWelcome is now "Bonjour!"
変数とは異なり、定数の値は設定後に変更できません。変更しようとすると、コードのコンパイル時にエラーとして報告されます。
let languageName = "Swift"
languageName = "Swift++"
// This is a compile-time error: languageName cannot be changed.
定数と変数の出力
print(_:separator:terminator:)関数を使用して、定数または変数の現在の値を出力できます。
print(friendlyWelcome)
// Prints "Bonjour!"
print(_:separator:terminator:)関数は、1つ以上の値を適切な出力先に出力するグローバル関数です。たとえばXcodeでは、print(_:separator:terminator:)関数はXcodeの「コンソール」ペインにその出力を表示します。separatorとterminatorパラメータにはデフォルト値があるため、この関数を呼び出すときに省略できます。デフォルトでは、関数は出力する行の末尾に改行を追加して終了します。改行なしで値を出力するには、ターミネータとして空の文字列を渡します。たとえば、print(someValue, terminator: "")のようにします。デフォルト値を持つパラメータについては、デフォルトパラメータ値を参照してください。
Swiftは文字列補間を使用して、定数または変数の名前をより長い文字列内のプレースホルダーとして含め、Swiftにそれをその定数または変数の現在の値で置き換えるよう促します。名前を括弧で囲み、開き括弧の前にバックスラッシュでエスケープします。
print("The current value of friendlyWelcome is \(friendlyWelcome)")
// Prints "The current value of friendlyWelcome is Bonjour!"
注意 文字列補間で使用できるすべてのオプションについては、文字列補間で説明しています。
コメント
コードにメモやリマインダーとして実行されないテキストを含めるには、コメントを使用します。コメントは、コードのコンパイル時にSwiftコンパイラによって無視されます。
Swiftのコメントは、Cのコメントと非常によく似ています。単一行コメントは、2つのスラッシュ(//)で始まります。
// This is a comment.
複数行コメントは、スラッシュの後にアスタリスク(/*)で始まり、アスタリスクの後にスラッシュ(*/)で終わります。
/* This is also a comment
but is written over multiple lines. */
Cの複数行コメントとは異なり、Swiftの複数行コメントは他の複数行コメント内にネストできます。ネストされたコメントを記述するには、複数行コメントブロックを開始し、最初のブロック内で2番目の複数行コメントを開始します。次に、2番目のブロックを閉じ、その後最初のブロックを閉じます。
/* This is the start of the first multiline comment.
/* This is the second, nested multiline comment. */
This is the end of the first multiline comment. */
ネストされた複数行コメントを使用すると、コードに既に複数行コメントが含まれている場合でも、大きなコードブロックをすばやく簡単にコメントアウトできます。
セミコロン
他の多くの言語とは異なり、Swiftはコード内の各文の後にセミコロン(;)を記述する必要はありませんが、必要に応じて記述することもできます。ただし、1行に複数の別々の文を記述する場合は、セミコロンが必要です。
let cat = "🐱"; print(cat)
// Prints "🐱".
整数
整数は、42や-23のような小数部分のない整数です。整数は符号付き(正、ゼロ、または負)または符号なし(正またはゼロ)のいずれかであり、その最大値と最小値はサイズ(値を保存するために使用されるビット数)に依存します。整数型は、その名前にサイズと符号を含みます。たとえば、8ビットの符号なし整数はUInt8型であり、32ビットの符号付き整数はInt32型です。Swiftのすべての型と同様に、これらの整数型は大文字で始まる名前を持ちます。ほとんどの場合、正確な整数サイズを指定する必要がない場合は、以下で説明するInt型を使用します。
整数型は、手計算で行うほとんどの算術と同じように動作します。整数の計算は近似なしで結果を生成します。これらの特性により、整数はカウントや正確な量を表すその他の計算に適しています。たとえば、テキストファイル内の最長行を見つける、ゲームでスコア乗数を適用する、またはレシートの価格を合計するなどです。
整数には小数部分がありませんが、小数部分を持つ量を表すために整数を使用できます。これは、小数部分をカウントすることで実現します。たとえば、セントをカウントする整数に数値123を保存することで、$1.23を表すことができます。このアプローチは固定小数点演算と呼ばれ、小数点が数値内の固定位置にあるためです。上記の例では、数値123は最後の2桁の前に小数点があると理解されます。
注意 金融や建設などの規制された分野での計算、または高精度の結果が期待される分野では、その分野の要件に従った丸めや切り捨てなどの動作を実装する特殊な数値型が必要になる場合があります。
整数の境界
各整数型の最小値と最大値には、そのminプロパティとmaxプロパティでアクセスできます。
let minValue = UInt8.min // minValue is equal to 0, and is of type UInt8
let maxValue = UInt8.max // maxValue is equal to 255, and is of type UInt8
これらのプロパティの値は、適切なサイズの数値型(上記の例ではUInt8など)であり、したがって同じ型の他の値とともに式で使用できます。
maxプロパティより大きい数値など、範囲外の結果を生成する計算は、無効な結果を保存する代わりにプログラムの実行を停止します。オーバーフロー演算子で説明されているように、明示的に演算をオーバーフローさせることもできます。
Int
ほとんどの場合、コードで使用する整数の特定のサイズを選択する必要はありません。Swiftは追加の整数型Intを提供し、これは現在のプラットフォームのネイティブワードサイズと同じサイズを持ちます。
- 32ビットプラットフォームでは、
IntはInt32と同じサイズです。 - 64ビットプラットフォームでは、
IntはInt64と同じサイズです。
特定のサイズの整数を扱う必要がない限り、コード内の整数値には常にIntを使用してください。これにより、コードの一貫性と相互運用性が向上します。32ビットプラットフォームでも、Intは-2,147,483,648から2,147,483,647までの任意の値を保存でき、多くの整数範囲に対して十分な大きさです。
UInt
Swiftは符号なし整数型UIntも提供し、これは現在のプラットフォームのネイティブワードサイズと同じサイズを持ちます。
- 32ビットプラットフォームでは、
UIntはUInt32と同じサイズです。 - 64ビットプラットフォームでは、
UIntはUInt64と同じサイズです。
注意
UIntは、プラットフォームのネイティブワードサイズと同じサイズの符号なし整数型が特に必要な場合にのみ使用してください。そうでない場合は、保存される値が非負であることがわかっている場合でも、Intが推奨されます。整数値に対するIntの一貫した使用は、コードの相互運用性を向上させ、異なる数値型間の変換の必要性を回避し、型安全性と型推論で説明されているように、整数型の推論と一致します。
浮動小数点数
浮動小数点数は、3.14159、0.1、-273.15のような小数部分を持つ数値です。Swiftは、整数のサイズが異なるのと同様に、異なるサイズの数値をサポートするさまざまな浮動小数点型を提供します。正確なサイズを指定する必要がない場合は、Doubleを使用してください。それ以外の場合は、Float16やFloat80など、必要なサイズをその名前に含む型を使用してください。浮動小数点演算の一般的な用語に従い、Floatは32ビットを使用し、Doubleは64ビットを使用します。これらの型はFloat32またはFloat64と書くこともできます。たとえば、グラフィックスコードでは、GPUの最速データ型に合わせてFloatがよく使用されます。一部の浮動小数点型は特定のプラットフォームでのみサポートされていますが、FloatとDoubleはすべてのプラットフォームで利用可能です。
浮動小数点数を使用すると、非常に小さな数値から非常に大きな数値まで扱うことができますが、その範囲内のすべての可能な値を表すことはできません。常に正確な結果を生成する整数計算とは異なり、浮動小数点演算は最も近い表現可能な数値に結果を丸めます。たとえば、数値10,000をFloatとして保存する場合、次に大きく表現できる数値は10,000.001です。これら2つの数値の間の値は、どちらか一方に丸められます。数値間の間隔も可変です。大きな数値の間には小さな数値の間よりも大きな間隔があります。たとえば、0.001の次のFloat値は0.0010000002であり、10,000の後の間隔よりも小さいです。
浮動小数点数には、計算でのオーバーフローとアンダーフローを表す負のゼロ、無限大、負の無限大の値があります。また、ゼロをゼロで除算するなどの無効または未定義の結果を表す非数(NaN)値も含まれます。この動作は、結果を表現できない場合にプログラムを停止する整数とは異なります。
すべての可能な値の間で同じ間隔が必要な場合、または実行する計算で正確な結果が必要で上記の特殊値を必要としない場合、浮動小数点数は適切なデータ型ではない可能性があります。代わりに、整数で説明されているように、固定小数点数の使用を検討してください。
型安全性と型推論
Swiftプログラムのすべての値には型があります。定数、変数、プロパティを含む、値を保存するすべての場所にも型があります。型アノテーションを使用して型を明示的に記述するか、Swiftが初期値から型を推論することがあります。コード内で値を提供するすべての場所で、その値の型は使用する場所と一致する必要があります。たとえば、コードの一部がStringを必要とする場合、誤ってIntを渡すことはできません。この種のチェックにより、Swiftは型安全な言語になります。
型安全な言語は、コードが扱う値の型を明確にすることを奨励します。ある型の値が別の型に暗黙的に変換されることはありません。ただし、一部の型は明示的に変換できます。コードをビルドする際、Swiftはコードの型安全性をチェックし、型の不一致をエラーとしてフラグを立てます。
型チェックは、異なる型の値を扱う際のエラーを回避するのに役立ちます。ただし、これは宣言するすべての定数と変数の型を指定する必要があることを意味するわけではありません。必要な値の型を指定しない場合、Swiftは型推論を使用して適切な型を決定します。型推論により、コンパイラは提供された値を調べることで、コードをコンパイルする際に特定の式の型を自動的に推論できます。
型推論のおかげで、SwiftはCやObjective-Cなどの言語よりもはるかに少ない型宣言で済みます。定数と変数は依然として明示的に型付けされていますが、その型を指定する作業の多くは自動的に行われます。
型推論は、初期値を持つ定数または変数を宣言する際に特に役立ちます。これは通常、宣言時に定数または変数にリテラル値(またはリテラル)を割り当てることで行われます。(リテラル値は、以下の例の42や3.14159のように、ソースコードに直接表示される値です。)
たとえば、型を指定せずに新しい定数にリテラル値42を割り当てると、Swiftは整数のように見える数値で初期化したため、定数をIntにしたいと推論します。
let meaningOfLife = 42
// meaningOfLife is inferred to be of type Int
同様に、浮動小数点リテラルの型を指定しない場合、SwiftはDoubleを作成したいと推論します。
let pi = 3.14159
// pi is inferred to be of type Double
Swiftは浮動小数点数の型を推論する際、常に(Floatではなく)Doubleを選択します。
式で整数リテラルと浮動小数点リテラルを組み合わせると、コンテキストからDouble型が推論されます。
let anotherPi = 3 + 0.14159
// anotherPi is also inferred to be of type Double
リテラル値3はそれ自体では明示的な型を持たないため、加算の一部として浮動小数点リテラルが存在することから、適切な出力型としてDoubleが推論されます。
数値リテラル
整数リテラルは次のように記述できます。
- プレフィックスなしの10進数
0bプレフィックス付きの2進数0oプレフィックス付きの8進数0xプレフィックス付きの16進数
これらの整数リテラルはすべて、10進値17を持ちます。
let decimalInteger = 17
let binaryInteger = 0b10001 // 17 in binary notation
let octalInteger = 0o21 // 17 in octal notation
let hexadecimalInteger = 0x11 // 17 in hexadecimal notation
浮動小数点リテラルは、10進数(プレフィックスなし)または16進数(0xプレフィックス付き)にできます。小数点の両側に常に数値(または16進数)が必要です。10進浮動小数点数にはオプションの指数を持つこともでき、大文字または小文字のeで示されます。16進浮動小数点数には指数が必須であり、大文字または小文字のpで示されます。
指数がxの10進数の場合、基数に10ˣを掛けます。
1.25e2は1.25 x 10²、つまり125.0を意味します。1.25e-2は1.25 x 10⁻²、つまり0.0125を意味します。
指数がxの16進数の場合、基数に2ˣを掛けます。
0xFp2は15 x 2²、つまり60.0を意味します。0xFp-2は15 x 2⁻²、つまり3.75を意味します。
これらの浮動小数点リテラルはすべて、10進値12.1875を持ちます。
let decimalDouble = 12.1875
let exponentDouble = 1.21875e1
let hexadecimalDouble = 0xC.3p0
数値リテラルには、読みやすくするための追加のフォーマットを含めることができます。整数と浮動小数点数の両方に余分なゼロを追加でき、読みやすさを向上させるためにアンダースコアを含めることができます。どちらのフォーマットもリテラルの基になる値には影響しません。
let paddedDouble = 000123.456
let oneMillion = 1_000_000
let justOverOneMillion = 1_000_000.000_000_1
数値型の変換
コード内のすべての汎用整数定数と変数には、非負であることがわかっている場合でも、Int型を使用してください。日常的な状況でデフォルトの整数型を使用することは、整数定数と変数がコード内ですぐに相互運用可能であり、整数リテラル値の推論された型と一致することを意味します。
外部ソースから明示的にサイズが決められたデータのため、パフォーマンス、メモリ使用量、またはその他の必要な最適化のために、タスクに特に必要な場合にのみ、他の整数型を使用してください。これらの状況で明示的にサイズが決められた型を使用すると、偶発的な値のオーバーフローをキャッチし、使用されているデータの性質を暗黙的に文書化するのに役立ちます。
整数の変換
整数定数または変数に保存できる数値の範囲は、各数値型で異なります。Int8定数または変数は-128から127までの数値を保存でき、UInt8定数または変数は0から255までの数値を保存できます。サイズ指定された整数型の定数または変数に収まらない数値は、コードのコンパイル時にエラーとして報告されます。
let cannotBeNegative: UInt8 = -1
// UInt8 can't store negative numbers, and so this will report an error
let tooBig: Int8 = Int8.max + 1
// Int8 can't store a number larger than its maximum value,
// and so this will also report an error
各数値型は異なる範囲の値を保存できるため、数値型の変換はケースバイケースでオプトインする必要があります。このオプトインアプローチは、隠れた変換エラーを防ぎ、コード内で型変換の意図を明示的にするのに役立ちます。
ある特定の数値型を別の型に変換するには、既存の値で目的の型の新しい数値を初期化します。以下の例では、定数twoThousandはUInt16型であり、定数oneはUInt8型です。同じ型ではないため、直接加算することはできません。代わりに、この例ではUInt16(one)を呼び出してoneの値で初期化された新しいUInt16を作成し、元の値の代わりにこの値を使用します。
let twoThousand: UInt16 = 2_000
let one: UInt8 = 1
let twoThousandAndOne = twoThousand + UInt16(one)
加算の両側がUInt16型になったため、加算が許可されます。出力定数(twoThousandAndOne)は、2つのUInt16値の合計であるため、UInt16型であると推論されます。
SomeType(ofInitialValue)は、Swift型のイニシャライザを呼び出して初期値を渡すデフォルトの方法です。内部的には、UInt16にはUInt8値を受け入れるイニシャライザがあり、このイニシャライザを使用して既存のUInt8から新しいUInt16を作成します。ただし、ここに任意の型を渡すことはできません。UInt16がイニシャライザを提供する型である必要があります。新しい型(独自の型定義を含む)を受け入れるイニシャライザを提供するために既存の型を拡張することについては、拡張で説明しています。
整数と浮動小数点数の変換
整数と浮動小数点の数値型間の変換は明示的に行う必要があります。
let three = 3
let pointOneFourOneFiveNine = 0.14159
let pi = Double(three) + pointOneFourOneFiveNine
// pi equals 3.14159, and is inferred to be of type Double
ここでは、定数threeの値を使用してDouble型の新しい値を作成し、加算の両側が同じ型になるようにしています。この変換がなければ、加算は許可されません。
浮動小数点から整数への変換も明示的に行う必要があります。整数型はDoubleまたはFloat値で初期化できます。
let integerPi = Int(pi)
// integerPi equals 3, and is inferred to be of type Int
この方法で新しい整数値を初期化するために使用される場合、浮動小数点値は常に切り捨てられます。これは、4.75は4になり、-3.9は-3になることを意味します。
注意 数値定数と変数を組み合わせるルールは、数値リテラルのルールとは異なります。リテラル値
3は、数値リテラルがそれ自体では明示的な型を持たないため、リテラル値0.14159に直接加算できます。その型は、コンパイラによって評価される時点でのみ推論されます。
型エイリアス
型エイリアスは、既存の型の代替名を定義します。型エイリアスはtypealiasキーワードで定義します。
型エイリアスは、外部ソースからの特定のサイズのデータを扱う場合など、文脈上より適切な名前で既存の型を参照したい場合に便利です。
typealias AudioSample = UInt16
型エイリアスを定義すると、元の名前を使用できる場所であればどこでもエイリアスを使用できます。
var maxAmplitudeFound = AudioSample.min
// maxAmplitudeFound is now 0
ここでは、AudioSampleはUInt16のエイリアスとして定義されています。エイリアスであるため、AudioSample.minの呼び出しは実際にはUInt16.minを呼び出し、maxAmplitudeFound変数に初期値0を提供します。
ブール値
SwiftにはBoolと呼ばれる基本的なブール型があります。ブール値は論理値と呼ばれ、真または偽のいずれかしかありません。Swiftは2つのブール定数値、trueとfalseを提供します。
let orangesAreOrange = true
let turnipsAreDelicious = false
orangesAreOrangeとturnipsAreDeliciousの型は、ブールリテラル値で初期化されたという事実からBoolと推論されています。上記のIntとDoubleと同様に、作成時にtrueまたはfalseに設定する場合、定数または変数をBoolとして宣言する必要はありません。
ブール値は、if文などの条件文を扱う際に特に役立ちます。
if turnipsAreDelicious {
print("Mmm, tasty turnips!")
} else {
print("Eww, turnips are horrible.")
}
// Prints "Eww, turnips are horrible."
if文などの条件文については、制御フローで詳しく説明しています。
Swiftの型安全性により、非ブール値をBoolの代わりに使用することはできません。次の例はコンパイル時エラーを報告します。
let i = 1
if i {
// this example will not compile, and will report an error
}
ただし、以下の代替例は有効です。
let i = 1
if i == 1 {
// this example will compile successfully
}
i == 1の比較の結果はBool型であるため、この2番目の例は型チェックに合格します。i == 1のような比較については、基本演算子で説明しています。
Swiftの他の型安全性の例と同様に、このアプローチは偶発的なエラーを回避し、コードの特定のセクションの意図が常に明確であることを保証します。
タプル
タプルは複数の値を単一の複合値にグループ化します。タプル内の値は任意の型にでき、互いに同じ型である必要はありません。
この例では、(404, "Not Found")はHTTPステータスコードを記述するタプルです。HTTPステータスコードは、Webページをリクエストするたびにウェブサーバーから返される特別な値です。リクエストしたWebページが存在しない場合、ステータスコード404 Not Foundが返されます。
let http404Error = (404, "Not Found")
// http404Error is of type (Int, String), and equals (404, "Not Found")
(404, "Not Found")タプルは、IntとStringをグループ化して、HTTPステータスコードに数値と人間が読める説明の2つの別々の値を与えます。これは「(Int, String)型のタプル」と説明できます。
任意の型の順列からタプルを作成でき、必要な数の異なる型を含めることができます。(Int, Int, Int)型、(String, Bool)型、または実際に必要な他の任意の順列のタプルを作成することを妨げるものは何もありません。
タプルの内容を別々の定数または変数に分解して、通常どおりアクセスできます。
let (statusCode, statusMessage) = http404Error
print("The status code is \(statusCode)")
// Prints "The status code is 404".
print("The status message is \(statusMessage)")
// Prints "The status message is Not Found".
タプルの一部の値だけが必要な場合は、タプルを分解する際にアンダースコア(_)でタプルの一部を無視できます。
let (justTheStatusCode, _) = http404Error
print("The status code is \(justTheStatusCode)")
// Prints "The status code is 404".
または、ゼロから始まるインデックス番号を使用して、タプル内の個々の要素値にアクセスできます。
print("The status code is \(http404Error.0)")
// Prints "The status code is 404".
print("The status message is \(http404Error.1)")
// Prints "The status message is Not Found".
タプルが定義されるときに、タプル内の個々の要素に名前を付けることができます。
let http200Status = (statusCode: 200, description: "OK")
タプル内の要素に名前を付けると、要素名を使用してそれらの要素の値にアクセスできます。
print("The status code is \(http200Status.statusCode)")
// Prints "The status code is 200".
print("The status message is \(http200Status.description)")
// Prints "The status message is OK".
タプルは、関数の戻り値として特に役立ちます。Webページを取得しようとする関数は、ページ取得の成功または失敗を記述するために(Int, String)タプル型を返すことがあります。それぞれ異なる型の2つの別々の値を持つタプルを返すことで、関数は単一の型の単一の値しか返せない場合よりも、その結果についてより有用な情報を提供します。詳細については、複数の戻り値を持つ関数を参照してください。
注意 タプルは、関連する値の単純なグループに便利です。複雑なデータ構造の作成には適していません。データ構造がより複雑になる可能性がある場合は、タプルではなくクラスまたは構造体としてモデル化してください。詳細については、構造体とクラスを参照してください。
オプショナル
値が存在しない可能性がある状況ではオプショナルを使用します。オプショナルは2つの可能性を表します。指定された型の値があるので、オプショナルをアンラップしてその値にアクセスできる、または値がまったくないのいずれかです。
欠落している可能性のある値の例として、SwiftのInt型にはString値をInt値に変換しようとするイニシャライザがあります。ただし、一部の文字列のみが整数に変換できます。文字列"123"は数値123に変換できますが、文字列"hello, world"には対応する数値がありません。以下の例では、イニシャライザを使用してStringをIntに変換しようとしています。
let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
// The type of convertedNumber is "optional Int"
上記のコードのイニシャライザは失敗する可能性があるため、IntではなくオプショナルのIntを返します。
オプショナル型を記述するには、オプショナルに含まれる型の名前の後に疑問符(?)を記述します。たとえば、オプショナルのIntの型はInt?です。オプショナルのIntは常に何らかのInt値を含むか、まったく値を含みません。BoolやString値など、他のものを含めることはできません。
nil
オプショナル変数を値のない状態に設定するには、特別な値nilを割り当てます。
var serverResponseCode: Int? = 404
// serverResponseCode contains an actual Int value of 404
serverResponseCode = nil
// serverResponseCode now contains no value
デフォルト値を指定せずにオプショナル変数を定義すると、変数は自動的にnilに設定されます。
var surveyAnswer: String?
// surveyAnswer is automatically set to nil
if文を使用して、オプショナルをnilと比較することで、オプショナルに値が含まれているかどうかを確認できます。この比較は、「等しい」演算子(==)または「等しくない」演算子(!=)で実行します。
オプショナルに値がある場合、nilと「等しくない」と見なされます。
let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
if convertedNumber != nil {
print("convertedNumber contains some integer value.")
}
// Prints "convertedNumber contains some integer value."
非オプショナルの定数または変数でnilを使用することはできません。コード内の定数または変数が特定の条件下で値の不在を処理する必要がある場合は、適切な型のオプショナル値として宣言してください。非オプショナル値として宣言された定数または変数は、nil値を含まないことが保証されます。非オプショナル値にnilを割り当てようとすると、コンパイル時エラーが発生します。
オプショナルと非オプショナル値のこの分離により、どの情報が欠落する可能性があるかを明示的にマークでき、欠落した値を処理するコードを記述しやすくなります。この間違いはコンパイル時にエラーを生成するため、誤ってオプショナルを非オプショナルであるかのように扱うことはできません。値をアンラップした後、その値を扱う他のコードはnilをチェックする必要がないため、コードの異なる部分で同じ値を繰り返しチェックする必要はありません。
オプショナル値にアクセスする際、コードは常にnilと非nilの両方のケースを処理します。値が欠落している場合にできることがいくつかあります。以下のセクションで説明します。
- 値が
nilのときに値に対して動作するコードをスキップする。 - オプショナルチェーンで説明されている
?.演算子を使用して、nilを返すかnil値を伝播する。 ??演算子を使用してフォールバック値を提供する。!演算子を使用してプログラムの実行を停止する。
注意 Objective-Cでは、
nilは存在しないオブジェクトへのポインタです。Swiftでは、nilはポインタではなく、特定の型の値の不在です。オブジェクト型だけでなく、任意の型のオプショナルをnilに設定できます。
オプショナルバインディング
オプショナルバインディングを使用して、オプショナルに値が含まれているかどうかを確認し、含まれている場合はその値を一時的な定数または変数として利用可能にします。オプショナルバインディングは、if、guard、while文とともに使用して、オプショナル内の値をチェックし、その値を単一のアクションの一部として定数または変数に抽出できます。if、guard、while文の詳細については、制御フローを参照してください。
if文のオプショナルバインディングは次のように記述します。
if let <#constantName#> = <#someOptional#> {
<#statements#>
}
オプショナルセクションの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")
}
// Prints "The string "123" has an integer value of 123".
このコードは次のように読むことができます。
「Int(possibleNumber)によって返されるオプショナルのIntに値が含まれている場合、actualNumberという新しい定数をオプショナルに含まれる値に設定します。」
変換が成功すると、if文の最初の分岐内でactualNumber定数が使用可能になります。オプショナル内に含まれる値で既に初期化されており、対応する非オプショナル型を持っています。この場合、possibleNumberの型はInt?なので、actualNumberの型はIntです。
オプショナルに含まれる値にアクセスした後、元のオプショナル定数または変数を参照する必要がない場合は、新しい定数または変数に同じ名前を使用できます。
let myNumber = Int(possibleNumber)
// Here, myNumber is an optional integer
if let myNumber = myNumber {
// Here, myNumber is a non-optional integer
print("My number is \(myNumber)")
}
// Prints "My number is 123".
このコードは、前の例のコードと同様に、myNumberに値が含まれているかどうかをチェックすることから始まります。myNumberに値がある場合、myNumberという名前の新しい定数の値がその値に設定されます。if文の本体内では、myNumberと書くとその新しい非オプショナル定数を参照します。if文の前または後にmyNumberと書くと、元のオプショナル整数定数を参照します。
この種のコードは非常に一般的なため、オプショナル値をアンラップするための短い記法を使用できます。アンラップする定数または変数の名前だけを記述します。新しいアンラップされた定数または変数は、暗黙的にオプショナル値と同じ名前を使用します。
if let myNumber {
print("My number is \(myNumber)")
}
// Prints "My number is 123".
オプショナルバインディングでは定数と変数の両方を使用できます。if文の最初の分岐内でmyNumberの値を操作したい場合は、代わりにif var myNumberと記述すると、オプショナルに含まれる値は定数ではなく変数として使用可能になります。if文の本体内でmyNumberに加えた変更は、アンラップした元のオプショナル定数または変数ではなく、そのローカル変数にのみ適用されます。
単一のif文に必要な数のオプショナルバインディングとブール条件を含めることができ、カンマで区切ります。オプショナルバインディングの値のいずれかがnilであるか、ブール条件のいずれかがfalseと評価されると、if文全体の条件はfalseと見なされます。次のif文は同等です。
if let firstNumber = Int("4"), let secondNumber = Int("42"), firstNumber < secondNumber && secondNumber < 100 {
print("\(firstNumber) < \(secondNumber) < 100")
}
// Prints "4 < 42 < 100".
if let firstNumber = Int("4") {
if let secondNumber = Int("42") {
if firstNumber < secondNumber && secondNumber < 100 {
print("\(firstNumber) < \(secondNumber) < 100")
}
}
}
// Prints "4 < 42 < 100".
if文でオプショナルバインディングで作成された定数と変数は、if文の本体内でのみ使用可能です。対照的に、guard文で作成された定数と変数は、早期終了で説明されているように、guard文に続くコード行で使用可能です。
フォールバック値の提供
欠落した値を処理する別の方法は、nil合体演算子(??)を使用してデフォルト値を提供することです。??の左側のオプショナルがnilでない場合、その値はアンラップされて使用されます。それ以外の場合は、??の右側の値が使用されます。たとえば、以下のコードは、名前が指定されている場合は名前で挨拶し、名前がnilの場合は一般的な挨拶を使用します。
let name: String? = nil
let greeting = "Hello, " + (name ?? "friend") + "!"
print(greeting)
// Prints "Hello, friend!"
??を使用してフォールバック値を提供する方法の詳細については、Nil合体演算子を参照してください。
強制アンラップ
プログラマーエラーや破損した状態など、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が常に値を含むことに依存しています。上記のいずれかのアプローチを使用して、コードの一部としてその要件を記述することで、コードは実行時にその要件が真であることを確認できます。
実行時のデータ要件の強制と仮定の確認の詳細については、アサーションと前提条件を参照してください。
暗黙的にアンラップされたオプショナル
上記で説明したように、オプショナルは定数または変数が「値なし」を持つことが許可されていることを示します。オプショナルはif文でチェックして値が存在するかどうかを確認でき、オプショナルバインディングで条件付きアンラップして、値が存在する場合はオプショナルの値にアクセスできます。
プログラムの構造から、その値が最初に設定された後、オプショナルが常に値を持つことが明らかな場合があります。このような場合、アクセスするたびにオプショナルの値をチェックしてアンラップする必要をなくすと便利です。常に値を持つと安全に想定できるためです。
この種のオプショナルは暗黙的にアンラップされたオプショナルとして定義されます。暗黙的にアンラップされたオプショナルを記述するには、オプショナルにしたい型の後に疑問符(String?)ではなく感嘆符(String!)を置きます。使用時にオプショナルの名前の後に感嘆符を置くのではなく、宣言時にオプショナルの型の後に感嘆符を置きます。
暗黙的にアンラップされたオプショナルは、オプショナルの値がオプショナルが最初に定義された直後に存在することが確認され、その後のすべての時点で確実に存在すると想定できる場合に便利です。Swiftでの暗黙的にアンラップされたオプショナルの主な用途は、非所有参照と暗黙的にアンラップされたオプショナルプロパティで説明されているように、クラスの初期化中です。
変数が後でnilになる可能性がある場合は、暗黙的にアンラップされたオプショナルを使用しないでください。変数の存続期間中にnil値をチェックする必要がある場合は、常に通常のオプショナル型を使用してください。
暗黙的にアンラップされたオプショナルは、内部的には通常のオプショナルですが、アクセスするたびにオプショナル値をアンラップする必要なく、非オプショナル値のように使用することもできます。次の例は、ラップされた値に明示的なStringとしてアクセスする際の、オプショナル文字列と暗黙的にアンラップされたオプショナル文字列の動作の違いを示しています。
let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // Requires explicit unwrapping
let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString // Unwrapped automatically
暗黙的にアンラップされたオプショナルは、必要に応じてオプショナルを強制アンラップする許可を与えるものと考えることができます。暗黙的にアンラップされたオプショナル値を使用する場合、Swiftはまず通常のオプショナル値として使用しようとします。オプショナルとして使用できない場合、Swiftは値を強制アンラップします。上記のコードでは、implicitStringは明示的な非オプショナル型のStringを持っているため、オプショナル値assumedStringはimplicitStringに値を割り当てる前に強制アンラップされます。以下のコードでは、optionalStringは明示的な型を持たないため、通常のオプショナルです。
let optionalString = assumedString
// The type of optionalString is "String?" and assumedString isn't force-unwrapped.
暗黙的にアンラップされたオプショナルがnilで、ラップされた値にアクセスしようとすると、ランタイムエラーが発生します。結果は、値を含まない通常のオプショナルを強制アンラップするために感嘆符を記述した場合とまったく同じです。
通常のオプショナルをチェックするのと同じ方法で、暗黙的にアンラップされたオプショナルがnilかどうかをチェックできます。
if assumedString != nil {
print(assumedString!)
}
// Prints "An implicitly unwrapped optional string."
オプショナルバインディングで暗黙的にアンラップされたオプショナルを使用して、単一の文でその値をチェックしてアンラップすることもできます。
if let definiteString = assumedString {
print(definiteString)
}
// Prints "An implicitly unwrapped optional string."
メモリ安全性
上記の型安全性と型推論で説明した型の不一致を防ぐチェックに加えて、Swiftは無効なメモリでの作業からコードを保護します。この保護はメモリ安全性として知られ、次の要件が含まれます。
- 値は読み取られる前に設定される。初期化されていないメモリ領域との相互作用に対する保護は、確定初期化としても知られています。
- 配列とバッファは有効なインデックスでのみアクセスされる。範囲外アクセスに対する保護は、境界安全性としても知られています。
- メモリは値の存続期間中にのみアクセスされる。use-after-freeエラーに対する保護は、存続期間安全性としても知られています。
- メモリへのアクセスは証明可能な安全な方法でのみ重複する。並行コードでの可能なデータ競合に対する保護は、スレッド安全性としても知られています。
これらの保証を提供しない言語で作業したことがある場合、上記のリストに記載されているエラーやバグのいくつかに馴染みがあるかもしれません。これらの問題に遭遇したことがなくても大丈夫です。Swiftの安全なコードはこれらの問題を回避します。初期値の設定をSwiftがどのように保証するかについては初期化を、並行コードでSwiftがメモリ安全性をどのようにチェックするかについては並行性を、メモリへの重複アクセスをSwiftがどのようにチェックするかについてはメモリ安全性を参照してください。
言語や標準ライブラリの制限のため、安全性の境界の外で作業する必要がある場合があります。そのため、Swiftは一部のAPIのアンセーフバージョンも提供しています。名前に「unsafe」、「unchecked」、「unmanaged」などの単語を含む型やメソッドを使用する場合、安全性の責任を負うことになります。
Swiftの安全なコードでも、エラーや予期しない失敗が発生し、プログラムの実行が停止する可能性があります。安全性はコードが完了まで実行されることを保証するものではありません。Swiftは、以下のエラー処理とアサーションと前提条件で説明されているように、エラーを示して回復するいくつかの方法を提供しています。ただし、場合によっては、エラーを処理する唯一の安全な方法は実行を停止することです。サービスが予期せず停止しないことを保証する必要がある場合は、コンポーネントが予期せず停止した場合に回復できるように、全体的なアーキテクチャにフォールトトレランスを組み込んでください。
エラー処理
エラー処理を使用して、プログラムの実行中に発生する可能性のあるエラー条件に対応します。
関数の成功または失敗を伝えるために値の有無を使用できるオプショナルとは対照的に、エラー処理を使用すると、失敗の根本的な原因を特定し、必要に応じてエラーをプログラムの別の部分に伝播できます。
関数がエラー条件に遭遇すると、エラーをスローします。その関数の呼び出し元はエラーをキャッチして適切に対応できます。
func canThrowAnError() throws {
// this function may or may not throw an error
}
関数は、宣言にthrowsキーワードを含めることで、エラーをスローできることを示します。エラーをスローできる関数を呼び出す場合、式の前にtryキーワードを付けます。
Swiftは、エラーがcatch句で処理されるまで、現在のスコープから自動的にエラーを伝播します。
do {
try canThrowAnError()
// no error was thrown
} catch {
// an error was thrown
}
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ケースと一致する場合、washDishes()関数が呼び出されます。エラーがスローされ、SandwichError.missingIngredientsケースと一致する場合、catchパターンによってキャプチャされた関連付けられた[String]値とともにbuyGroceries(_:)関数が呼び出されます。
エラーのスロー、キャッチ、伝播については、エラー処理で詳しく説明しています。
アサーションと前提条件
アサーションと前提条件は、実行時に発生するチェックです。これらを使用して、追加のコードを実行する前に必須の条件が満たされていることを確認します。アサーションまたは前提条件のブール条件がtrueと評価されると、コードの実行は通常どおり続行されます。条件がfalseと評価されると、プログラムの現在の状態は無効です。コードの実行は終了し、アプリは終了します。
アサーションと前提条件を使用して、コーディング中に行った仮定と期待を表現するため、コードの一部として含めることができます。アサーションは開発中に間違いと誤った仮定を見つけるのに役立ち、前提条件は本番環境で問題を検出するのに役立ちます。
上記のエラー処理で説明したエラー条件とは異なり、アサーションと前提条件は回復可能または予期されるエラーには使用されません。アサーションまたは前提条件の失敗は無効なプログラム状態を示すため、失敗したアサーションをキャッチする方法はありません。無効な状態からの回復は不可能です。アサーションが失敗すると、プログラムのデータの少なくとも1つが無効になりますが、なぜ無効なのか、追加の状態も無効かどうかはわかりません。
アサーションと前提条件の使用は、無効な条件が発生しにくいようにコードを設計することの代わりにはなりません。ただし、それらを使用して有効なデータと状態を強制すると、無効な状態が発生した場合にアプリがより予測可能に終了し、問題のデバッグが容易になります。仮定がチェックされない場合、他の場所のコードが明らかに失敗し始めるまで、この種の問題に気づかず、ユーザーデータが静かに破損した後になることがあります。無効な状態が検出されたらすぐに実行を停止することで、その無効な状態によって引き起こされる損害を制限するのにも役立ちます。
アサーションと前提条件の違いは、チェックされるタイミングです。アサーションはデバッグビルドでのみチェックされますが、前提条件はデバッグビルドと本番ビルドの両方でチェックされます。本番ビルドでは、アサーション内の条件は評価されません。これは、開発プロセス中に本番環境のパフォーマンスに影響を与えることなく、必要な数のアサーションを使用できることを意味します。
アサーションによるデバッグ
Swift標準ライブラリのassert(::file:line:)関数を呼び出してアサーションを記述します。この関数にtrueまたはfalseと評価される式と、条件の結果がfalseの場合に表示するメッセージを渡します。例:
let age = -3
assert(age >= 0, "A person's age can't be less than zero.")
// This assertion fails because -3 isn't >= 0.
この例では、age >= 0がtrueと評価される場合、つまりageの値が非負の場合、コードの実行が続行されます。ageの値が負の場合、上記のコードのように、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.")
}
前提条件の強制
条件が偽になる可能性があるが、コードの実行を続行するには必ず真でなければならない場合は、前提条件を使用します。たとえば、前提条件を使用して、添え字が範囲外でないことをチェックしたり、関数に有効な値が渡されたことをチェックしたりします。
precondition(::file:line:)関数を呼び出して前提条件を記述します。この関数にtrueまたはfalseと評価される式と、条件の結果がfalseの場合に表示するメッセージを渡します。例:
// In the implementation of a subscript...
precondition(index > 0, "Index must be greater than zero.")
preconditionFailure(_:file:line:)関数を呼び出して、失敗が発生したことを示すこともできます。たとえば、switchのデフォルトケースが取られたが、すべての有効な入力データがswitchの他のケースのいずれかで処理されているはずの場合などです。
注意 チェックなしモード(
-Ounchecked)でコンパイルすると、前提条件はチェックされません。コンパイラは前提条件が常に真であると仮定し、それに応じてコードを最適化します。ただし、fatalError(_:file:line:)関数は、最適化設定に関係なく、常に実行を停止します。プロトタイピングと初期開発中に、
fatalError("Unimplemented")をスタブ実装として記述することで、まだ実装されていない機能のスタブを作成するためにfatalError(_:file:line:)関数を使用できます。致命的なエラーはアサーションや前提条件とは異なり、決して最適化されないため、スタブ実装に遭遇した場合は必ず実行が停止することが保証されます。