Swift 言語ガイド – 1. 基礎

swift

はじめに

公式ページのSwift 言語ガイド「The Swift Programming Language Swift 5.3」に基づき記載いたします。本ページでは、「基礎」について記載いたします。
記載内容に誤り等ございましたら、ご連絡をいただければ幸いです。

1. 基礎

Swiftは、iOS、macOS、watchOS、およびtvOSアプリ開発用の新しいプログラミング言語です。 それでも、Swiftの多くの部分は、CおよびObjective-Cでの開発経験から親しみやすいものです。

Swiftは、整数用のInt、浮動小数点値用のDoubleおよびFloat、ブール値用のBool、テキストデータ用のStringなど、すべての基本的なCおよびObjective-Cタイプの独自のバージョンを提供します。Swiftは、コレクションタイプで説明されているように、3つの主要なコレクションタイプ、配列、セット、および辞書の強力なバージョンも提供します。

Cと同様に、Swiftは変数を使用して、識別名で値を格納および参照します。 Swiftは、値を変更できない変数も多用します。これらは定数と呼ばれ、Cの定数よりもはるかに強力です。定数は、変更する必要のない値を操作するときにコードをより安全で明確にするためにSwift全体で使用されます。

使い慣れたタイプに加えて、Swiftでは、タプルなど、Objective-Cにはない高度なタイプが導入されています。タプルを使用すると、値のグループを作成して渡すことができます。タプルを使用して、関数から複数の値を単一の複合値として返すことができます。

Swiftには、値がないことを処理するオプショナルタイプも導入されています。 オプショナルは、「値があり、それがxに等しい」または「値がまったくない」のいずれかを示します。オプションの使用は、Objective-Cのポインターでnilを使用するのと似ていますが、クラスだけでなく、すべてのタイプで機能します。オプショナルは、Objective-Cのnilポインターよりも安全で表現力に優れているだけでなく、Swiftの最も強力な機能の多くの中心にあります。

Swiftはタイプセーフな言語です。つまり、この言語は、コードで使用できる値のタイプを明確にするのに役立ちます。コードの一部に文字列が必要な場合、型安全性により、誤ってIntを渡さないようにします。同様に、型の安全性は、オプショナルの文字列を、オプショナルではない文字列を必要とするコードに誤って渡すことを防ぎます。型安全性は、開発プロセスのできるだけ早い段階でエラーを見つけて修正するのに役立ちます。

1.1. 定数と変数

定数と変数は、名前(maximumNumberOfLoginAttemptsやwelcomeMessageなど)を特定のタイプの値(数値10や文字列 “Hello”など)に関連付けます。定数の値は一度設定すると変更できませんが、変数は将来別の値に設定できます。

1.1.1. 定数と変数の宣言

定数と変数は、使用する前に宣言する必要があります。 定数はletキーワードで宣言し、変数はvarキーワードで宣言します。定数と変数を使用して、ユーザが行ったログイン試行の回数を追跡する方法の例を次に示します。

let maximumNumberOfLoginAttempts = 10
var currentLoginAttempt = 0

このコードは次のように読み取ることができます。

「maximumNumberOfLoginAttemptsという新しい定数を宣言し、それに値10を与えます。次に、currentLoginAttemptという新しい変数を宣言し、それに初期値0を与えます。」

この例では、最大値が変更されないため、許可されるログイン試行の最大数が定数として宣言されています。現在のログイン試行カウンターは変数として宣言されています。これは、ログイン試行が失敗するたびにこの値をインクリメントする必要があるためです。

複数の定数または複数の変数を、コンマで区切って1行で宣言できます。

var x = 0.0, y = 0.0, z = 0.0

注意
コードに格納されている値が変更されない場合は、常にletキーワードを使用して定数として宣言してください。変数は、変更できる必要がある値を格納するためにのみ使用してください。

1.2. 型注釈

定数または変数を宣言するときに型注釈を指定して、定数または変数が格納できる値の種類を明確にすることができます。定数名または変数名の後にコロン、スペース、使用する型の名前を続けて、型注釈を記述します。

この例では、welcomeMessageという変数の型注釈を与えて、変数が文字列値を格納できることを示します。

var welcomeMessage: String

宣言のコロンは「…型…」を意味するため、上記のコードは次のように読み取ることができます。

「String型のwelcomeMessageという変数を宣言します。」

「文字列型」という句は、「任意の文字列値を格納できる」ことを意味します。保存できる「物の型」(または「物の種類」)を意味すると考えてください。

welcomeMessage変数は、エラーなしに任意の文字列値を設定できるようになりました。

welcomeMessage = "Hello"

同じ型の複数の関連する変数を、コンマで区切って1行に定義し、最後の変数名の後に単一の型注釈を付けることができます。

var red, green, blue: Double

注意
実際に型注釈を記述する必要があることはめったにありません。定義された時点で定数または変数の初期値を指定すると、型安全性と型推論で説明されているように、Swiftはほとんどの場合、その定数または変数に使用される型を推測できます。上記のwelcomeMessageの例では、初期値が指定されていないため、welcomeMessage変数の型は、初期値から推測されるのではなく、型アノテーションで指定されます。

1.3. 定数と変数の命名

定数名と変数名には、Unicode文字を含むほぼすべての文字を含めることができます。

let π = 3.14159
let 你好 = "你好世界"
let 🐶🐮 = "dogcow"

定数名と変数名には、空白文字、数学記号、矢印、私的使用のUnicodeスカラー値、または罫線とボックス描画文字を含めることはできません。また、名前のどこかに数字が含まれている場合もありますが、数字で始めることはできません。

特定の型の定数または変数を宣言すると、同じ名前で再度宣言したり、別の型の値を格納するように変更したりすることはできません。また、定数を変数に変更したり、変数を定数に変更したりすることもできません。

注意
予約済みのSwiftキーワードと同じ名前を定数または変数に付ける必要がある場合は、名前として使用するときにキーワードをバッククォート( `)で囲みます。 ただし、絶対に選択の余地がない限り、名前としてキーワードを使用することは避けてください。

既存の変数の値を互換性のあるタイプの別の値に変更できます。この例では、friendlyWelcomeの値が「Hello!」から「ボンジュール!」へ変更されています。

var friendlyWelcome = "Hello!"
friendlyWelcome = "Bonjour!"
// friendlyWelcomeは、"Bonjour!"です

変数とは異なり、定数の値は設定後に変更できません。そうしようとすると、コードのコンパイル時にエラーとして報告されます。

let languageName = "Swift"
languageName = "Swift++"
// これはコンパイル時のエラーです:languageNameは変更できません。

1.4. 定数と変数の出力

print(_:separator:terminator:)関数を使用して、定数または変数の現在の値を出力できます。

print(friendlyWelcome)
// 「Hello!」を出力します

print(:separator:terminator:)関数は、1つ以上の値を適切な出力に出力するグローバル関数です。たとえば、Xcodeでは、print(:separator:terminator:)関数は、Xcodeの「コンソール」ペインに出力を出力します。separatorとterminatorのパラメーターにはデフォルト値があるため、この関数を呼び出すときにそれらを省略できます。デフォルトでは、関数は改行を追加して印刷する行を終了します。後に改行なしで値を出力するには、ターミネータとして空の文字列を渡します(例:print(someValue、terminator:””))。デフォルト値のパラメーターについては、「デフォルトのパラメーター値」を参照してください。

Swiftは文字列補間を使用して、定数または変数の名前をより長い文字列のプレースホルダーとして含め、Swiftにその定数または変数の現在の値に置き換えるように促します。 名前を括弧で囲み、開始括弧の前に円記号でエスケープします。

print("The current value of friendlyWelcome is \(friendlyWelcome)")
// "The current value of friendlyWelcome is Bonjour!"を出力します。

注意
文字列補間で使用できるすべてのオプションは、文字列補間で説明されています。

1.5. コメント

コメントを使用して、実行不可能なテキストをコードに含め、メモまたはリマインダーとして使用します。コードがコンパイルされるとき、コメントはSwiftコンパイラによって無視されます。

Swiftのコメントは、Cのコメントと非常によく似ています。1行のコメントは2つのスラッシュ(//)で始まります。

// これはコメントです

複数行コメントは、スラッシュとそれに続くアスタリスク(/*)で始まり、アスタリスクとそれに続くスラッシュ(*/)で終わります。

/* これもコメントです
しかし、複数行にわたって書かれています。*/

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. */

ネストされた複数行コメントを使用すると、コードにすでに複数行コメントが含まれている場合でも、コードの大きなブロックをすばやく簡単にコメントアウトできます。

1.6. セミコロン

他の多くの言語とは異なりSwiftでは、コード内の各ステートメントの後にセミコロン(;)を記述する必要はありませんが、必要に応じて記述することができます。ただし、1行に複数の個別のステートメントを記述したい場合は、セミコロンが必要です。

1.7. 整数

整数は、42や-23などの小数成分を含まない整数です。整数は、符号付き(正、ゼロ、または負)または符号なし(正またはゼロ)のいずれかです。

Swiftは、8、16、32、および64ビット形式の符号付きおよび符号なし整数を提供します。これらの整数は、8ビットの符号なし整数がUInt8型であり、32ビットの符号付き整数がInt32型であるという点で、Cと同様の命名規則に従います。Swiftのすべての型と同様に、これらの整数型には大文字の名前が付いています。

1.7.1. 整数の境界

minプロパティとmaxプロパティを使用して、各整数タイプの最小値と最大値にアクセスできます。

 let minValue = UInt8.min // minValueは0に等しく、UInt8型です。
 let maxValue = UInt8.max // maxValueは255に等しく、UInt8型です。

これらのプロパティの値は適切なサイズの数値型(上記の例のUInt8など)であるため、同じ型の他の値と一緒に式で使用できます。

1.7.2. Int

ほとんどの場合、コードで使用する整数の特定のサイズを選択する必要はありません。Swiftは、現在のプラットフォームのネイティブワードサイズと同じサイズの追加の整数型Intを提供します。

  • 32ビットプラットフォームでは、IntはInt32と同じサイズです。
  • 64ビットプラットフォームでは、IntはInt64と同じサイズです。

特定のサイズの整数で作業する必要がない限り、コードの整数値には常にIntを使用してください。これにより、コードの一貫性と相互運用性が向上します。32ビットプラットフォームでも、Intは-2,147,483,648から2,147,483,647までの任意の値を格納でき、多くの整数範囲に十分な大きさです。

1.7.3. UInt

Swiftは、現在のプラットフォームのネイティブワードサイズと同じサイズの符号なし整数型UIntも提供します。

  • 32ビットプラットフォームでは、UIntはUInt32と同じサイズです。
  • 64ビットプラットフォームでは、UIntはUInt64と同じサイズです。

注意
UIntは、プラットフォームのネイティブワードサイズと同じサイズの符号なし整数型が特に必要な場合にのみ使用してください。そうでない場合は、格納される値が負でないことがわかっている場合でも、Intが優先されます。整数値にIntを一貫して使用すると、コードの相互運用性が向上し、型の安全性と型推論で説明されているように、異なる数値型間で変換する必要がなくなり、整数型の推論と一致します。

1.8. 浮動小数点数

浮動小数点数は、3.14159、0.1、-273.15などの小数成分を持つ数です。

浮動小数点型は、整数型よりもはるかに広い範囲の値を表すことができ、Intに格納できるよりもはるかに大きいまたは小さい数値を格納できます。Swiftは、2つの符号付き浮動小数点数タイプを提供します。

  • Doubleは、64ビット浮動小数点数を表します。
  • Floatは、32ビットの浮動小数点数を表します。

注意
Doubleの精度は10進数で15桁以上ですが、Floatの精度は10進数で6桁です。使用する適切な浮動小数点型は、コードで使用する必要のある値の性質と範囲によって異なります。どちらのタイプも適切な状況では、Doubleが推奨されます。

1.9. 型安全性と型推論

Swiftはタイプセーフな言語です。タイプセーフ言語を使用すると、コードで使用できる値の型を明確にすることができます。コードの一部に文字列が必要な場合、誤ってIntに渡すことはできません。

Swiftはタイプセーフであるため、コードのコンパイル時に型チェックを実行し、不一致のタイプをエラーとしてフラグ付けします。これにより、開発プロセスのできるだけ早い段階でエラーを検出して修正できます。

型チェックは、さまざまなタイプの値を操作するときにエラーを回避するのに役立ちます。ただし、これは、宣言するすべての定数と変数の型を指定する必要があるという意味ではありません。必要な値の型を指定しない場合、Swiftは型推論を使用して適切なタイプを計算します。型推論を使用すると、コンパイラーは、コードをコンパイルするときに、指定した値を調べるだけで、特定の式の型を自動的に推測できます。

型推論のため、SwiftはCやObjective-Cなどの言語よりもはるかに少ない型宣言が必要です。定数と変数は引き続き明示的に型指定されますが、それらの型を指定する作業の多くはユーザーに代わって行われます。

型推論は、定数または変数を初期値で宣言するときに特に役立ちます。これは多くの場合、宣言した時点で定数または変数にリテラル値(またはリテラル)を割り当てることによって行われます。(リテラル値は、以下の例の42や3.14159など、ソースコードに直接表示される値です。)

たとえば、42のリテラル値を新しい定数に割り当て、そのタイプを指定しない場合、Swiftは、整数のように見える数値で定数を初期化したため、定数をIntにしたいと推測します。

let meaningOfLife = 42
// meanOfLifeはInt型であると推測されます

同様に、浮動小数点リテラルの型を指定しない場合、SwiftはDoubleを作成することを推測します。

let pi = 3.14159
// piはDouble型であると推測されます

Swiftは、浮動小数点数のタイプを推測するときに、常に(Floatではなく)Doubleを選択します。

式で整数リテラルと浮動小数点リテラルを組み合わせると、コンテキストからDouble型が推測されます。

let anotherPi = 3 + 0.14159
// anotherPiもDouble型であると推測されます

リテラル値3には、それ自体に明示的な型がないため、加算の一部としての浮動小数点リテラルの存在から、適切な出力のDouble型が推測されます。

1.10. 数値リテラル

整数リテラルは次のように記述できます。

  • プレフィックスのない10進数
  • プレフィックスが0bの2進数
  • プレフィックスが0oの8進数
  • プレフィックスが0xの16進数

これらの整数リテラルはすべて、10進値が17です。

let decimalInteger = 17
let binaryInteger = 0b10001 // バイナリ表記で17
let octalInteger = 0o21 // 8進表記で17
let hexadecimalInteger = 0x11 // 16進表記で17

浮動小数点リテラルは、10進数(接頭辞なし)または16進数(接頭辞0x付き)にすることができます。常に小数点の両側に数値(または16進数)が必要です。10進数の浮動小数点数には、大文字または小文字のeで示されるオプションの指数を含めることもできます。16進浮動小数点数には、大文字または小文字のpで示される指数が必要です。

指数がexpの10進数の場合、基数に10expを掛けます。

1.25e2は、1.25 x 102、つまり125.0を意味します。
1.25e-2は、1.25 x 10-2、つまり0.0125を意味します。

指数がexpの16進数の場合、基数に2expを掛けます。

0xFp2は、15 x 22、つまり60.0を意味します。
0xFp-2は、15 x 2-2、つまり3.75を意味します

これらの浮動小数点リテラルはすべて、12.1875の10進値を持っています。

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

1.11. 数値型変換

非負であることがわかっている場合でも、コード内のすべての汎用整数定数と変数にはInt型を使用します。通常、デフォルトの整数型を使用するということは、整数定数と変数がコード内ですぐに相互運用可能であり、整数リテラル値の推定型と一致することを意味します。

他の整数型は、外部ソースからの明示的なサイズのデータのため、またはパフォーマンス、メモリ使用量、またはその他の必要な最適化のために、タスクに特に必要な場合にのみ使用してください。このような状況で明示的にサイズ設定された型を使用すると、偶発的な値のオーバーフローをキャッチし、使用されているデータの性質を暗黙的に文書化するのに役立ちます。

1.11.1. 整数変換

整数定数または整数変数に格納できる数値の範囲は、数値タイプごとに異なります。Int8定数または変数は、-128〜127の数値を格納できますが、UInt8定数または変数は、0〜255の数値を格納できます。サイズ付き整数型の定数または変数に収まらない数値は、コードのコンパイル時にエラーとして報告されます。

let cannotBeNegative: UInt8 = -1
// UInt8は負の数を格納できないため、エラーが報告されます
let tooBig: Int8 = Int8.max + 1
// Int8は、最大値より大きい数値を格納できません。
// これもエラーを報告します

各数値型は異なる範囲の値を格納できるため、ケースバイケースで数値型変換をオプトインする必要があります。このオプトインアプローチは、隠れた変換エラーを防ぎ、型変換の意図をコードで明示的にするのに役立ちます。

ある特定の数値タイプを別の数値タイプに変換するには、目的のタイプの新しい数値を既存の値で初期化します。以下の例では、定数2000はタイプUInt16であり、定数1はタイプ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が初期化子を提供する型である必要があります。既存の型を拡張して、新しい型(独自の型定義を含む)を受け入れる初期化子を提供する方法については、拡張機能で説明しています。

1.11.2. 整数および浮動小数点変換

整数型と浮動小数点数値型の間の変換は、明示的に行う必要があります。

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になることを意味します。

注意
数値定数と変数を組み合わせるための規則は、数値リテラルの規則とは異なります。数値リテラルにはそれ自体に明示的な型がないため、リテラル値3をリテラル値0.14159に直接追加できます。それらの型は、コンパイラによって評価された時点でのみ推測されます。

1.12. 型エイリアス

型エイリアスは、既存の型の代替名を定義します。typealiasキーワードを使用して型エイリアスを定義します。

型エイリアスは、外部ソースからの特定のサイズのデータを操作する場合など、コンテキストにより適切な名前で既存の型を参照する場合に役立ちます。

typealias AudioSample = UInt16

型エイリアスを定義すると、元の名前を使用できる場所であればどこでもエイリアスを使用できます。

var maxAmplitudeFound = AudioSample.min
// maxAmplitudeFoundは0になりました

ここで、AudioSampleは、UInt16のエイリアスとして定義されています。これはエイリアスであるため、AudioSample.minの呼び出しは、実際にはUInt16.minを呼び出します。これにより、maxAmplitudeFound変数の初期値が0になります。

1.13. ブール値

Swiftには、Boolと呼ばれる基本的なブール型があります。ブール値は、真または偽にしかできないため、論理値と呼ばれます。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ステートメントなどの条件ステートメントについては、制御フローで詳しく説明しています。

Swiftの型安全性は、ブール値以外の値がBoolの代わりになるのを防ぎます。次の例は、コンパイル時エラーを報告します。

let i = 1
if i {
    // この例はコンパイルされず、エラーを報告します
}

ただし、以下の代替例は有効です。

let i = 1
if i == 1 {
    // この例は正常にコンパイルされます
}

i == 1の比較の結果はBool型であるため、この2番目の例は型チェックに合格します。 i == 1のような比較については、基本演算子で説明しています。

Swiftの他の型安全性の例と同様に、このアプローチは偶発的なエラーを回避し、コードの特定のセクションの意図が常に明確であることを保証します。

1.14. タプル

タプルは、複数の値を1つの複合値にグループ化します。タプル内の値は任意のタイプにすることができ、互いに同じタイプである必要はありません。

この例では、(404、「見つかりません」)はHTTPステータスコードを記述するタプルです。HTTPステータスコードは、Webページを要求するたびにWebサーバーから返される特別な値です。存在しないウェブページをリクエストすると、ステータスコード404 NotFoundが返されます。

let http404Error = (404, "Not Found")
// http404Errorは(Int、String)型であり、(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)")
// 「ステータスコードは404です」と出力します
print("The status message is (statusMessage)")
// 「ステータスメッセージが見つかりません」を出力します

タプルの値の一部のみが必要な場合は、タプルを分解するときにアンダースコア(_)が付いているタプルの部分を無視します。

let (justTheStatusCode, _) = http404Error
print("The status code is (justTheStatusCode)")
// 「ステータスコードは404です」と出力します

または、ゼロから始まるインデックス番号を使用して、タプル内の個々の要素値にアクセスします。

print("The status code is (http404Error.0)")
// 「ステータスコードは404です」と出力します
print("The status message is (http404Error.1)")
// 「ステータスメッセージが見つかりません」を出力します

タプルが定義されているときに、タプル内の個々の要素に名前を付けることができます。

let http200Status = (statusCode: 200, description: "OK")

タプル内の要素に名前を付ける場合は、要素名を使用してそれらの要素の値にアクセスできます。

print("The status code is (http200Status.statusCode)")
// 「ステータスコードは200です」と出力します
print("The status message is (http200Status.description)")
// 「ステータスメッセージはOKです」と出力します

タプルは、関数の戻り値として特に役立ちます。Webページを取得しようとする関数は、ページ取得の成功または失敗を説明するために(Int, String)タプル型を返す場合があります。それぞれが異なる型の2つの異なる値を持つタプルを返すことにより、関数は、単一の型の単一の値しか返すことができない場合よりも、その結果に関するより有用な情報を提供します。詳細については、複数の戻り値を持つ関数を参照してください。

注意
タプルは、関連する値の単純なグループに役立ちます。複雑なデータ構造の作成には適していません。データ構造がより複雑になる可能性がある場合は、タプルとしてではなく、クラスまたは構造体としてモデル化します。詳細については、「構造体とクラス」を参照してください。

値が存在しない可能性がある状況では、オプショナルを使用します。オプショナルは2つの可能性を表します。値があり、オプショナルをアンラップしてその値にアクセスできるか、値がまったくないかのいずれかです。

注意
オプショナルの概念は、CまたはObjective-Cには存在しません。Objective-Cで最も近いのは、オブジェクトを返すメソッドからnilを返す機能です。nilは「有効なオブジェクトがない」ことを意味します。ただし、これはオブジェクトに対してのみ機能します。構造体、基本的なCタイプ、または列挙値に対しては機能しません。これらのタイプの場合、Objective-Cメソッドは通常、値がないことを示す特別な値(NSNotFoundなど)を返します。このアプローチは、メソッドの呼び出し元が、テストする特別な値があることを知っており、それをチェックすることを忘れないことを前提としています。Swiftのオプショナルを使用すると、特別な定数を必要とせずに、どの型にも値がないことを示すことができます。

1.15. オプショナル

これは、オプショナルを使用して値がないことに対処する方法の例です。SwiftのInt型には、文字列値をInt値に変換しようとする初期化子があります。ただし、すべての文字列を整数に変換できるわけではありません。文字列「123」は数値123に変換できますが、文字列「hello、world」には、変換する明確な数値がありません。

以下の例では、初期化子を使用して文字列をIntに変換しています。

let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
// convertNumberは、「Int?」または「オプショナルのInt」型であると推測されます。

イニシャライザは失敗する可能性があるため、IntではなくオプショナルのIntを返します。オプショナルのIntは、IntではなくInt?として記述されます。疑問符は、含まれる値がオプショナルであることを示します。つまり、Int値が含まれる場合と、値がまったく含まれない場合があります。(Bool値やString値など、他のものを含めることはできません。Intであるか、まったく何もありません。)

1.15.1. nil

オプショナル変数に特別な値nilを割り当てることにより、値のない状態に設定します。

var serverResponseCode: Int? = 404
// serverResponseCodeには、404の実際のInt値が含まれています
serverResponseCode = nil
// serverResponseCodeに値が含まれていません

注意
オプショナルではない定数と変数でnilを使用することはできません。コード内の定数または変数が特定の条件下で値がない状態で機能する必要がある場合は、常に適切な型のオプショナル値として宣言してください。

デフォルト値を指定せずにオプショナル変数を定義すると、変数は自動的にnilに設定されます。

var surveyAnswer: String?
// SurveyAnswerは自動的にnilに設定されます

注意
Swiftのnilは、Objective-Cのnilと同じではありません。Objective-Cでは、nilは存在しないオブジェクトへのポインタです。Swiftでは、nilはポインターではなく、特定の型の値がないことを意味します。オブジェクト型だけでなく、任意の型のオプショナルをnilに設定できます。

1.15.2. Ifステートメントと強制アンラッピング

ifステートメントを使用して、オプショナルをnilと比較することにより、オプショナルに値が含まれているかどうかを確認できます。この比較は、「等しい」演算子(==)または「等しくない」演算子(!=)を使用して実行します。

オプショナルに値がある場合、nilと「等しくない」と見なされます。

if convertedNumber != nil {
    print("convertedNumber contains some integer value.")
}
// 「convertedNumberに整数値が含まれています」と出力します。

オプショナルに値が含まれていることを確認したら、オプショナルの名前の末尾に感嘆符(!)を追加することで、基になる値にアクセスできます。感嘆符は効果的に次のように述べています。「このオプショナルには間違いなく値があることを私は知っています。 使ってください。」 これは、オプショナル値の強制アンラップとして知られています。

if convertedNumber != nil {
    print("convertedNumber has an integer value of (convertedNumber!).")
}
// 「convertedNumberの整数値は123です」と出力されます。

ifステートメントの詳細については、制御フローを参照してください。

注意
使用しようとしています! 存在しないオプショナル値にアクセスすると、ランタイムエラーがトリガーされます。使用する前に、オプショナルにnil以外の値が含まれていることを常に確認してください。その値を強制的にアンラップします。

1.15.3. オプショナルバインディング

オプショナルバインディングを使用して、オプショナルに値が含まれているかどうかを確認し、含まれている場合は、その値を一時的な定数または変数として使用できるようにします。オプショナルバインディングをifステートメントおよびwhileステートメントとともに使用して、オプショナル内の値をチェックし、その値を単一のアクションの一部として定数または変数に抽出できます。ifおよび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)\" could not be converted to an integer")
}
// 「文字列「123」の整数値は123」と出力されます。

このコードは次のように読み取ることができます。

「Int(possibleNumber)によって返されるオプショナルのIntに値が含まれている場合は、actualNumberという新しい定数をオプショナルに含まれている値に設定します。」

変換が成功すると、actualNumber定数がifステートメントの最初のブランチで使用できるようになります。オプショナルに含まれる値ですでに初期化されているため、その値にアクセスするための接尾辞!を使用する必要はありません。この例では、actualNumberは単に変換の結果を出力するために使用されます。

オプショナルバインディングを使用して、定数と変数の両方を使用できます。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ステートメントの本体内でのみ使用できます。対照的に、ガードステートメントで作成された定数と変数は、早期終了で説明されているように、ガードステートメントに続くコード行で使用できます。

1.15.4. 暗黙的にアンラップされたオプショナル

上記のように、オプショナルは、定数または変数が「値なし」を持つことができることを示します。オプショナルは、ifステートメントでチェックして値が存在するかどうかを確認できます。また、オプショナルバインディングで条件付きでラップを解除して、オプショナルの値が存在する場合はそれにアクセスできます。

プログラムの構造から、値が最初に設定された後、オプショナルには常に値があることが明らかな場合があります。このような場合、オプショナルの値は常に値を持っていると安全に想定できるため、アクセスするたびにオプショナルの値を確認してアンラップする必要がなくなると便利です。

これらの種類のオプショナルは、暗黙的にアンラップされたオプショナルとして定義されます。オプショナルにしたい型の後に疑問符(String?)ではなく感嘆符(String!)を配置することにより、暗黙的にアンラップされたオプショナルを記述します。使用時にオプショナルの名前の後に感嘆符を配置するのではなく、宣言するときにオプションの型の後に感嘆符を配置します。

暗黙的にアンラップされたオプショナルは、オプショナルが最初に定義された直後にオプショナルの値が存在することが確認され、その後のすべてのポイントで確実に存在すると見なすことができる場合に役立ちます。Swiftでの暗黙的にアンラップされたオプショナルの主な用途は、「所有されていない参照」および「暗黙的にアンラップされたオプショナルのプロパティ」で説明されているように、クラスの初期化中です。

暗黙的にアンラップされたオプショナルは、バックグラウンドでの通常のオプショナルですが、アクセスするたびにオプショナル値をアンラップする必要なしに、非オプショナル値のように使用することもできます。次の例は、明示的な文字列としてラップされた値にアクセスする場合の、オプショナルの文字列と暗黙的にアンラップされたオプショナルの文字列の動作の違いを示しています。

let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // 感嘆符が必要です
let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString // 感嘆符は必要ありません

暗黙的にアンラップされたオプショナルは、必要に応じてオプショナルを強制的にアンラップする許可を与えると考えることができます。暗黙的にアンラップされたオプショナル値を使用する場合、Swiftは最初にそれを通常のオプショナル値として使用しようとします。オプショナルとして使用できない場合、Swiftは値を強制的にアンラップします。上記のコードでは、implicitStringには明示的なオプショナルではない型のStringがあるため、オプショナルの値assumeStringは、その値をimplicitStringに割り当てる前に強制的にアンラップされます。以下のコードでは、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."が出力されます

注意
後で変数がnilになる可能性がある場合は、暗黙的にアンラップされたオプショナルを使用しないでください。変数の存続期間中にnil値をチェックする必要がある場合は、常に通常のオプショナル型を使用してください。

1.16. エラー処理

エラー処理を使用して、実行中にプログラムで発生する可能性のあるエラー状態に対応します。

関数の成功または失敗を伝えるために値の有無を使用できるオプショナルとは対照的に、エラー処理では、失敗の根本的な原因を特定し、必要に応じて、プログラムの別の部分にエラーを伝播できます。

関数がエラー状態に遭遇すると、エラーをスローします。その関数の呼び出し元は、エラーをキャッチして適切に応答できます。

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の場合と一致する場合、washDishes()関数が呼び出されます。エラーがスローされ、SandwichError.missingIngredientsの場合と一致する場合、buyGroceries(_:)関数が呼び出され、関連する[String]値がcatchパターンによってキャプチャされます。

エラーのスロー、キャッチ、および伝播については、エラー処理で詳しく説明しています。

1.17. アサーションと前提条件

アサーションと前提条件は、実行時に行われるチェックです。これらを使用して、さらにコードを実行する前に、必須条件が満たされていることを確認します。アサーションまたは前提条件のブール条件がtrueと評価された場合、コードの実行は通常どおり続行されます。条件がfalseと評価された場合、プログラムの現在の状態は無効です。コードの実行が終了し、アプリが終了します。

アサーションと前提条件を使用して、コーディング中に行う仮定と期待を表現するため、それらをコードの一部として含めることができます。アサーションは、開発中に間違いや誤った仮定を見つけるのに役立ち、前提条件は、本番環境の問題を検出するのに役立ちます。

実行時に期待値を検証することに加えて、アサーションと前提条件は、コード内のドキュメントの便利な形式にもなります。上記のエラー処理で説明したエラー条件とは異なり、アサーションと前提条件は、回復可能なエラーまたは予想されるエラーには使用されません。失敗したアサーションまたは前提条件は無効なプログラム状態を示しているため、失敗したアサーションをキャッチする方法はありません。

アサーションと前提条件を使用することは、無効な状態が発生する可能性が低いような方法で、コードを設計することに代わるものではありません。ただし、それらを使用して有効なデータと状態を適用すると、無効な状態が発生した場合にアプリがより予測どおりに終了し、問題のデバッグが容易になります。無効な状態が検出されるとすぐに実行を停止することも、その無効な状態によって引き起こされる損害を制限するのに役立ちます。

アサーションと前提条件の違いは、チェックされるときです。アサーションはデバッグビルドでのみチェックされますが、前提条件はデバッグビルドと本番ビルドの両方でチェックされます。本番ビルドでは、アサーション内の条件は評価されません。つまり、本番環境のパフォーマンスに影響を与えることなく、開発プロセス中に必要な数のアサーションを使用できます。

1.17.1. アサーションを使用したデバッグ

Swift標準ライブラリからassert(_:_:file:line)関数を呼び出すことにより、アサーションを記述します。この関数に、trueまたはfalseと評価される式と、条件の結果がfalseの場合に表示するメッセージを渡します。 例えば:

let age = -3
assert(age >= 0, "A person's age can't be less than zero.")
// -3が> = 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.")
}

1.17.2. 前提条件の実施

条件がfalseになる可能性がある場合は常に前提条件を使用しますが、コードが実行を継続するには、必ずtrueである必要があります。たとえば、前提条件を使用して、添え字が範囲外にないことを確認したり、関数に有効な値が渡されたことを確認したりします。

precondition(_:_:file:line:)関数を呼び出すことにより、前提条件を記述します。この関数に、trueまたはfalseと評価される式と、条件の結果がfalseの場合に表示するメッセージを渡します。例えば:

// 下付き文字の実装では…
precondition(index > 0, "Index must be greater than zero.")

preconditionFailure(_:file:line:)関数を呼び出して、障害が発生したことを示すこともできます。たとえば、スイッチのデフォルトのケースが採用されたが、すべての有効な入力データがスイッチの他のケースの1つによって処理されている必要がある場合。

注意
チェックされていないモード(-Ounchecked)でコンパイルする場合、前提条件はチェックされません。コンパイラーは、前提条件が常に真であると想定し、それに応じてコードを最適化します。 ただし、fatalError(_:file:line:)関数は、最適化の設定に関係なく、常に実行を停止します。

プロトタイプ作成および初期開発中にfatalError(_:file:line:)関数を使用して、スタブ実装としてfatalError(“Unimplemented”)を記述することにより、まだ実装されていない機能のスタブを作成できます。アサーションや前提条件とは異なり、致命的なエラーが最適化されることはないため、スタブ実装が発生した場合に実行が常に停止することを確認できます。

タイトルとURLをコピーしました