今日のローカルTensorFlowEverywhereイベントの出欠確認!
このページは Cloud Translation API によって翻訳されました。
Switch to English

スウィフトツアー

Swift.orgのASwiftTourから変更を加えて適応。オリジナルのコンテンツはAppleInc。によって作成されました。CreativeCommonsAttribution4.0International(CC BY 4.0)ライセンスの下でライセンス供与されています。
TensorFlow.orgで表示 GoogleColabで実行 GitHubでソースを表示

伝統は、新しい言語の最初のプログラムは「Hello、world!」という言葉を印刷するべきだと示唆しています。画面上。 Swiftでは、これは1行で実行できます。

print("Hello, world!")
Hello, world!

CまたはObjective-Cでコードを記述した場合、この構文はおなじみのように見えます。Swiftでは、このコード行は完全なプログラムです。入出力や文字列処理などの機能のために、個別のライブラリをインポートする必要はありません。グローバルスコープで記述されたコードはプログラムのエントリポイントとして使用されるため、 main()関数は必要ありません。また、すべてのステートメントの最後にセミコロンを書く必要はありません。

このツアーでは、さまざまなプログラミングタスクを実行する方法を紹介することで、Swiftでコードを書き始めるのに十分な情報を提供します。何かがわからなくても心配しないでください。このツアーで紹介されていることはすべて、この本の残りの部分で詳しく説明されています。

単純な値

letを使用letて定数を作成し、 varを使用して変数を作成します。定数の値はコンパイル時に知る必要はありませんが、値を1回だけ割り当てる必要があります。つまり、定数を使用して、一度決定した値に名前を付けることができますが、多くの場所で使用できます。

var myVariable = 42
myVariable = 50
let myConstant = 42

定数または変数は、割り当てる値と同じ型である必要があります。ただし、必ずしも型を明示的に記述する必要はありません。定数または変数を作成するときに値を指定すると、コンパイラーはその型を推測できます。上記の例では、コンパイラは、初期値が整数であるため、 myVariableが整数であると推測します。

初期値で十分な情報が得られない場合(または初期値がない場合)は、変数の後にコロンで区切ってタイプを記述してください。注:浮動小数点数にFloat代わりにDoubleを使用すると、精度が向上し、Swiftのデフォルトの浮動小数点型になります。

let implicitInteger = 70
let implicitDouble = 70.0
let explicitDouble: Double = 70
// Experiment:
// Create a constant with an explicit type of `Float` and a value of 4.

値が暗黙的に別のタイプに変換されることはありません。値を別のタイプに変換する必要がある場合は、目的のタイプのインスタンスを明示的に作成してください。

let label = "The width is "
let width = 94
print(label + String(width))
The width is 94

// Experiment:
// Try removing the conversion to `String` from the last line. What error do you get?

文字列に値を含めるさらに簡単な方法があります。値を括弧で囲み、括弧の前に円記号( ``)を記述します。例えば:

let apples = 3
print("I have \(apples) apples.")
I have 3 apples.

let oranges = 5
print("I have \(apples + oranges) pieces of fruit.")
I have 8 pieces of fruit.

// Experiment:
// Use `\()` to include a floating-point calculation in a string and to include someone's name in a
// greeting.

複数行を占める文字列には、3つの二重引用符( """ )を使用します。引用符で囲まれた各行の先頭のインデントは、終了引用符のインデントと一致する限り削除されます。例:

let quotation = """
    Even though there's whitespace to the left,
    the actual lines aren't indented.
        Except for this line.
    Double quotes (") can appear without being escaped.

    I still have \(apples + oranges) pieces of fruit.
    """
print(quotation)
Even though there's whitespace to the left,
the actual lines aren't indented.
    Except for this line.
Double quotes (") can appear without being escaped.

I still have 8 pieces of fruit.

角かっこ( [] )を使用して配列と辞書を作成し、角かっこでインデックスまたはキーを書き込むことによってそれらの要素にアクセスします。最後の要素の後にコンマを入れることができます。

var shoppingList = ["catfish", "water", "tulips", "blue paint"]
shoppingList[1] = "bottle of water"

var occupations = [
    "Malcolm": "Captain",
    "Kaylee": "Mechanic",
]
occupations["Jayne"] = "Public Relations"
print(occupations)
["Jayne": "Public Relations", "Kaylee": "Mechanic", "Malcolm": "Captain"]

要素を追加すると、配列は自動的に大きくなります。

shoppingList.append("blue paint")
print(shoppingList)
["catfish", "bottle of water", "tulips", "blue paint", "blue paint"]

空の配列または辞書を作成するには、初期化構文を使用します。

let emptyArray = [String]()
let emptyDictionary = [String: Float]()

型情報を推測できる場合は、空の配列を[]として、空の辞書を[:]として書き込むことができます。たとえば、変数に新しい値を設定したり、関数に引数を渡したりする場合です。

shoppingList = []
occupations = [:]

制御フロー

使用ifswitch条件文を作成し、使用するfor - inforwhile 、とrepeat - whileループを作ります。条件変数またはループ変数を囲む括弧はオプションです。体の周りにブレースが必要です。

let individualScores = [75, 43, 103, 87, 12]
var teamScore = 0
for score in individualScores {
    if score > 50 {
        teamScore += 3
    } else {
        teamScore += 1
    }
}
print(teamScore)
11

ifステートメントでは、条件はブール式である必要があります。これは、 if score { ... }などのコードがエラーであり、ゼロとの暗黙的な比較ではないことを意味します。

ifletを使用しif 、欠落している可能性のある値を処理できます。これらの値はオプションとして表されます。オプションの値には、値が含まれているか、値が欠落していることを示すnilが含まれています。値のタイプの後に疑問符( ? )を記述して、値をオプションとしてマークします。

var optionalString: String? = "Hello"
print(optionalString == nil)
false

var optionalName: String? = "John Appleseed"
var greeting = "Hello!"
if let name = optionalName {
    greeting = "Hello, \(name)"
}
print(greeting)
Hello, John Appleseed

// Experiment:
// Change `optionalName` to `nil`. What greeting do you get?
// Add an `else` clause that sets a different greeting if `optionalName` is `nil`.

オプションの値がnil場合、条件はfalseなり、中括弧内のコードはスキップされます。それ以外の場合、オプションの値はラップ解除され、 let後に定数に割り当てられます。これにより、ラップ解除された値がコードブロック内で使用可能になります。

オプションの値を処理する別の方法は、 ??を使用してデフォルト値を提供することです。オペレーター。オプションの値が欠落している場合は、代わりにデフォルト値が使用されます。

let nickName: String? = nil
let fullName: String = "John Appleseed"
print("Hi \(nickName ?? fullName)")
Hi John Appleseed

スイッチは、あらゆる種類のデータとさまざまな比較操作をサポートします。整数や同等性のテストに限定されません。

let vegetable = "red pepper"
switch vegetable {
case "celery":
    print("Add some raisins and make ants on a log.")
case "cucumber", "watercress":
    print("That would make a good tea sandwich.")
case let x where x.hasSuffix("pepper"):
    print("Is it a spicy \(x)?")
default:
    print("Everything tastes good in soup.")
}
Is it a spicy red pepper?

// Experiment:
// Try removing the default case. What error do you get?

パターンでletを使用して、パターンのその部分に一致する値を定数に割り当てる方法に注目してください。

一致したswitchケース内のコードを実行した後、プログラムはswitchステートメントを終了します。実行は次のケースに続行されないため、各ケースのコードの最後で明示的にスイッチから抜け出す必要はありません。

for - inを使用して、キーと値のペアごとに使用する名前のペアを指定することにより、ディクショナリ内のアイテムを反復処理します。ディクショナリは順序付けられていないコレクションであるため、それらのキーと値は任意の順序で繰り返されます。

let interestingNumbers = [
    "Prime": [2, 3, 5, 7, 11, 13],
    "Fibonacci": [1, 1, 2, 3, 5, 8],
    "Square": [1, 4, 9, 16, 25],
]
var largest = 0
for (kind, numbers) in interestingNumbers {
    for number in numbers {
        if number > largest {
            largest = number
        }
    }
}
print(largest)
25

// Experiment:
// Add another variable to keep track of which kind of number was the largest, as well as what that
// largest number was.

whileを使用whileて、条件が変更されるまでコードのブロックを繰り返します。代わりに、ループの条件を最後にして、ループが少なくとも1回実行されるようにすることができます。

var n = 2
while n < 100 {
    n = n * 2
}

print(n)
128

var m = 2
repeat {
    m = m * 2
} while m < 100

print(m)
128

..<を使用してインデックスの範囲を作成するか、明示的な初期化、条件、およびインクリメントを記述することにより、インデックスをループ内に保持できます。これらの2つのループは同じことをします:

var total = 0
for i in 0..<4 {
    total += i
}

print(total)
6

..<を使用して上限値を省略した範囲を作成し、 ...を使用して両方の値を含む範囲を作成します。

機能と閉鎖

funcを使用してfuncを宣言します。名前の後に括弧で囲まれた引数のリストを付けて関数を呼び出します。 ->を使用して、パラメーターの名前と型を関数の戻り値の型から分離します。

func greet(name: String, day: String) -> String {
    return "Hello \(name), today is \(day)."
}
print(greet(name: "Bob", day: "Tuesday"))
Hello Bob, today is Tuesday.

// Experiment:
// Remove the `day` parameter. Add a parameter to include today’s lunch special in the greeting.

デフォルトでは、関数はパラメーター名を引数のラベルとして使用します。パラメータ名の前にカスタム引数ラベルを書き込むか、引数ラベルを使用しない場合は_を記述します。

func greet(_ person: String, on day: String) -> String {
    return "Hello \(person), today is \(day)."
}
print(greet("John", on: "Wednesday"))
Hello John, today is Wednesday.

タプルを使用して複合値を作成します。たとえば、関数から複数の値を返します。タプルの要素は、名前または番号のいずれかで参照できます。

func calculateStatistics(scores: [Int]) -> (min: Int, max: Int, sum: Int) {
    var min = scores[0]
    var max = scores[0]
    var sum = 0

    for score in scores {
        if score > max {
            max = score
        } else if score < min {
            min = score
        }
        sum += score
    }

    return (min, max, sum)
}
let statistics = calculateStatistics(scores: [5, 3, 100, 3, 9])
print(statistics.sum)
print(statistics.2)
120
120

関数はネストできます。入れ子関数は、外部関数で宣言された変数にアクセスできます。ネストされた関数を使用して、長い関数または複雑な関数にコードを整理できます。

func returnFifteen() -> Int {
    var y = 10
    func add() {
        y += 5
    }
    add()
    return y
}
print(returnFifteen())
15

関数はファーストクラスのタイプです。これは、関数がその値として別の関数を返すことができることを意味します。

func makeIncrementer() -> ((Int) -> Int) {
    func addOne(number: Int) -> Int {
        return 1 + number
    }
    return addOne
}
var increment = makeIncrementer()
print(increment(7))
8

関数は、その引数の1つとして別の関数を取ることができます。

func hasAnyMatches(list: [Int], condition: (Int) -> Bool) -> Bool {
    for item in list {
        if condition(item) {
            return true
        }
    }
    return false
}
func lessThanTen(number: Int) -> Bool {
    return number < 10
}
var numbers = [20, 19, 7, 12]
print(hasAnyMatches(list: numbers, condition: lessThanTen))
true

関数は実際にはクロージャの特殊なケースです。後で呼び出すことができるコードのブロックです。クロージャ内のコードは、クロージャが実行されたときに別のスコープにある場合でも、クロージャが作成されたスコープで使用可能だった変数や関数などにアクセスできます。この例は、すでにネストされた関数で見られます。コードを中括弧( {} )で囲むことにより、名前なしでクロージャーを記述できます。 inを使用inて、引数と戻り値の型を本体から分離します。

let mappedNumbers = numbers.map({ (number: Int) -> Int in
    let result = 3 * number
    return result
})
print(mappedNumbers)
[60, 57, 21, 36]

// Experiment:
// Rewrite the closure to return zero for all odd numbers.

クロージャをより簡潔に記述するためのいくつかのオプションがあります。デリゲートのコールバックなど、クロージャの型がすでにわかっている場合は、そのパラメータの型、戻り値の型、またはその両方を省略できます。単一ステートメントのクロージャは、それらの唯一のステートメントの値を暗黙的に返します。

let mappedNumbers2 = numbers.map({ number in 3 * number })
print(mappedNumbers2)
[60, 57, 21, 36]

名前ではなく番号でパラメータを参照できます。このアプローチは、非常に短いクロージャで特に役立ちます。関数の最後の引数として渡されたクロージャは、括弧の直後に表示できます。クロージャが関数の唯一の引数である場合は、括弧を完全に省略できます。

let sortedNumbers = numbers.sorted { $0 > $1 }
print(sortedNumbers)
[20, 19, 12, 7]

オブジェクトとクラス

classを作成するには、クラスの後にクラス名を使用します。クラス内のプロパティ宣言は、クラスのコンテキスト内にあることを除いて、定数宣言または変数宣言と同じ方法で記述されます。同様に、メソッド宣言と関数宣言は同じ方法で記述されます。

class Shape {
    var numberOfSides = 0
    func simpleDescription() -> String {
        return "A shape with \(numberOfSides) sides."
    }
}
// Experiment:
// Add a constant property with `let`, and add another method that takes an argument.

クラス名の後に括弧を付けて、クラスのインスタンスを作成します。ドット構文を使用して、インスタンスのプロパティとメソッドにアクセスします。

var shape = Shape()
shape.numberOfSides = 7
var shapeDescription = shape.simpleDescription()

このバージョンのShapeクラスには、重要なものがありません。インスタンスの作成時にクラスをセットアップするための初期化子です。 initを使用して作成します。

class NamedShape {
    var numberOfSides: Int = 0
    var name: String

    init(name: String) {
        self.name = name
    }

    func simpleDescription() -> String {
        return "A shape with \(numberOfSides) sides."
    }
}

nameプロパティを初期化子のname引数から区別するためにselfがどのように使用されているかに注目してください。初期化子への引数は、クラスのインスタンスを作成するときに関数呼び出しのように渡されます。すべてのプロパティには、宣言( numberOfSides )または初期化子( name )のいずれかで値を割り当てる必要があります。

オブジェクトの割り当てが解除される前にクリーンアップを実行する必要がある場合は、 deinitを使用してdeinitを作成します。

サブクラスには、クラス名の後にコロンで区切られたスーパークラス名が含まれます。クラスが標準のルートクラスをサブクラス化する必要はないため、必要に応じてスーパークラスを含めたり省略したりできます。

スーパークラスの実装をオーバーライドするサブクラスのメソッドは、オーバーライドでマークされoverrideoverrideせずに誤ってメソッドをoverrideすると、コンパイラーによってエラーとして検出されます。コンパイラーは、スーパークラスのどのメソッドも実際にはオーバーライドしないoverrideを持つメソッドも検出しoverride

class Square: NamedShape {
    var sideLength: Double

    init(sideLength: Double, name: String) {
        self.sideLength = sideLength
        super.init(name: name)
        numberOfSides = 4
    }

    func area() -> Double {
        return sideLength * sideLength
    }

    override func simpleDescription() -> String {
        return "A square with sides of length \(sideLength)."
    }
}
let test = Square(sideLength: 5.2, name: "my test square")
print(test.area())
print(test.simpleDescription())
27.040000000000003
A square with sides of length 5.2.

// Experiment:
// - Make another subclass of `NamedShape` called `Circle` that takes a radius and a name as
//   arguments to its initializer.
// - Implement an `area()` and a `simpleDescription()` method on the `Circle` class.

保存される単純なプロパティに加えて、プロパティにはゲッターとセッターを含めることができます。

class EquilateralTriangle: NamedShape {
    var sideLength: Double = 0.0

    init(sideLength: Double, name: String) {
        self.sideLength = sideLength
        super.init(name: name)
        numberOfSides = 3
    }

    var perimeter: Double {
        get {
            return 3.0 * sideLength
        }
        set {
            sideLength = newValue / 3.0
        }
    }

    override func simpleDescription() -> String {
        return "An equilateral triangle with sides of length \(sideLength)."
    }
}
var triangle = EquilateralTriangle(sideLength: 3.1, name: "a triangle")
print(triangle.perimeter)
triangle.perimeter = 9.9
print(triangle.sideLength)
9.3
3.3000000000000003

perimeterのセッターでは、新しい値の暗黙の名前はnewValueです。 set後に、括弧内に明示的な名前を指定できます。

EquilateralTriangleクラスの初期化子には3つの異なるステップがあることに注意してください。

  1. サブクラスが宣言するプロパティの値を設定します。

  2. スーパークラスの初期化子を呼び出します。

  3. スーパークラスによって定義されたプロパティの値を変更します。メソッド、ゲッター、またはセッターを使用する追加のセットアップ作業も、この時点で実行できます。

プロパティを計算する必要はないが、新しい値を設定する前後に実行されるコードを提供する必要がある場合は、 willSetdidSet使用します。指定したコードは、初期化子の外部で値が変更されるたびに実行されます。たとえば、以下のクラスは、三角形の辺の長さが常に正方形の辺の長さと同じであることを保証します。

class TriangleAndSquare {
    var triangle: EquilateralTriangle {
        willSet {
            square.sideLength = newValue.sideLength
        }
    }
    var square: Square {
        willSet {
            triangle.sideLength = newValue.sideLength
        }
    }
    init(size: Double, name: String) {
        square = Square(sideLength: size, name: name)
        triangle = EquilateralTriangle(sideLength: size, name: name)
    }
}
var triangleAndSquare = TriangleAndSquare(size: 10, name: "another test shape")
print(triangleAndSquare.square.sideLength)
print(triangleAndSquare.triangle.sideLength)
triangleAndSquare.square = Square(sideLength: 50, name: "larger square")
print(triangleAndSquare.triangle.sideLength)
10.0
10.0
50.0

オプションの値を使用する場合は、 ?書くことができます?メソッド、プロパティ、添え字などの操作の前。 ?前の値の場合nil?後のすべては無視され、式全体の値はnilです。それ以外の場合、オプションの値はラップ解除され、 ?後のすべてがアンラップされ?ラップされていない値に作用します。どちらの場合も、式全体の値はオプションの値です。

let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
print(optionalSquare?.sideLength)
Optional(2.5)

列挙と構造

enumを使用してenumを作成します。クラスや他のすべての名前付きタイプと同様に、列挙にはメソッドを関連付けることができます。

enum Rank: Int {
    case ace = 1
    case two, three, four, five, six, seven, eight, nine, ten
    case jack, queen, king

    func simpleDescription() -> String {
        switch self {
        case .ace:
            return "ace"
        case .jack:
            return "jack"
        case .queen:
            return "queen"
        case .king:
            return "king"
        default:
            return String(self.rawValue)
        }
    }
}
let ace = Rank.ace
print(ace)
let aceRawValue = ace.rawValue
print(aceRawValue)
ace
1

// Experiment:
// Write a function that compares two `Rank` values by comparing their raw values.

デフォルトでは、Swiftはゼロから始まり、毎回1ずつ増加する生の値を割り当てますが、値を明示的に指定することでこの動作を変更できます。上記の例では、Aceに1生の値が明示的に与えられ、残りの生の値が順番に割り当てられます。列挙型の生の型として文字列または浮動小数点数を使用することもできます。 rawValueプロパティを使用して、列挙ケースの生の値にアクセスします。

init?(rawValue:)初期化子を使用して、生の値から列挙型のインスタンスを作成します。生の値に一致する列挙ケースを返すか、一致するRankがない場合はnil返します。

if let convertedRank = Rank(rawValue: 3) {
    let threeDescription = convertedRank.simpleDescription()
}

列挙型のケース値は実際の値であり、生の値を書き込む別の方法ではありません。実際、意味のある生の値がない場合は、提供する必要はありません。

enum Suit {
    case spades, hearts, diamonds, clubs

    func simpleDescription() -> String {
        switch self {
        case .spades:
            return "spades"
        case .hearts:
            return "hearts"
        case .diamonds:
            return "diamonds"
        case .clubs:
            return "clubs"
        }
    }
}
let hearts = Suit.hearts
let heartsDescription = hearts.simpleDescription()
// Experiment:
// Add a `color()` method to `Suit` that returns "black" for spades and clubs, and returns "red" for
// hearts and diamonds.

列挙のHeartsケースが上記で参照されている2つの方法に注意してください。 hearts定数に値を割り当てる場合、定数には明示的な型が指定されていないため、列挙ケースSuit.HeartsSuit.Heartsで参照されます。スイッチ内では、 selfの値がスーツであることがすでにわかっているため、列挙ケースは省略形.Hearts参照されます。値のタイプがすでにわかっている場合はいつでも、省略形を使用できます。

列挙に生の値がある場合、それらの値は宣言の一部として決定されます。つまり、特定の列挙ケースのすべてのインスタンスは常に同じ生の値を持ちます。列挙ケースのもう1つの選択肢は、ケースに値を関連付けることです。これらの値はインスタンスの作成時に決定され、列挙ケースのインスタンスごとに異なる場合があります。関連する値は、列挙型ケースインスタンスの格納されたプロパティのように動作すると考えることができます。

たとえば、サーバーから日の出と日の入りの時刻を要求する場合を考えてみます。サーバーは、要求された情報で応答するか、何が問題だったかの説明で応答します。

enum ServerResponse {
    case result(String, String)
    case failure(String)
}

let success = ServerResponse.result("6:00 am", "8:09 pm")
let failure = ServerResponse.failure("Out of cheese.")

switch success {
case let .result(sunrise, sunset):
    print("Sunrise is at \(sunrise) and sunset is at \(sunset).")
case let .failure(message):
    print("Failure...  \(message)")
}
Sunrise is at 6:00 am and sunset is at 8:09 pm.

// Experiment:
// Add a third case to `ServerResponse` and to the switch.

値をスイッチケースと照合する一環として、 ServerResponse値から日の出と日の入りの時刻がどのように抽出されるかに注目してください。

structを使用してstructを作成します。構造体は、メソッドや初期化子など、クラスと同じ動作の多くをサポートします。構造体とクラスの最も重要な違いの1つは、構造体はコードで渡されるときに常にコピーされますが、クラスは参照によって渡されることです。

struct Card {
    var rank: Rank
    var suit: Suit
    func simpleDescription() -> String {
        return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
    }
}
let threeOfSpades = Card(rank: .three, suit: .spades)
let threeOfSpadesDescription = threeOfSpades.simpleDescription()
// Experiment:
// Write a function that returns an array containing a full deck of cards, with one card of each
// combination of rank and suit.

プロトコルと拡張機能

protocolを使用してprotocolを宣言します。

protocol ExampleProtocol {
    var simpleDescription: String { get }
    mutating func adjust()
}

クラス、列挙、および構造体はすべてプロトコルを採用できます。

class SimpleClass: ExampleProtocol {
    var simpleDescription: String = "A very simple class."
    var anotherProperty: Int = 69105
    func adjust() {
        simpleDescription += "  Now 100% adjusted."
    }
}
var a = SimpleClass()
a.adjust()
let aDescription = a.simpleDescription

struct SimpleStructure: ExampleProtocol {
    var simpleDescription: String = "A simple structure"
    mutating func adjust() {
        simpleDescription += " (adjusted)"
    }
}
var b = SimpleStructure()
print(b.adjust())
print(b.simpleDescription)
()
A simple structure (adjusted)

// Experiment:
// Add another requirement to `ExampleProtocol`.
// What changes do you need to make to `SimpleClass` and `SimpleStructure` so that they still
// conform to the protocol?

SimpleStructureの宣言でSimpleStructureキーワードを使用して、構造を変更するメソッドをマークしているmutating注意してください。 SimpleClassの宣言では、クラスのメソッドは常にクラスを変更できるため、変更としてマークされたメソッドは必要ありません。

extension機能を使用して、新しいメソッドや計算されたプロパティなど、既存の型に機能を追加します。拡張機能を使用して、他の場所で宣言されている型、またはライブラリやフレームワークからインポートした型にプロトコル適合性を追加できます。

extension Int: ExampleProtocol {
    public var simpleDescription: String {
        return "The number \(self)"
    }
    public mutating func adjust() {
        self += 42
    }
}
print(7.simpleDescription)
The number 7

// Experiment:
// Write an extension for the `Double` type that adds an `absoluteValue` property.

他の名前付きタイプと同じようにプロトコル名を使用できます。たとえば、異なるタイプを持つが、すべてが単一のプロトコルに準拠するオブジェクトのコレクションを作成できます。タイプがプロトコルタイプである値を操作する場合、プロトコル定義外のメソッドは使用できません。

let protocolValue: ExampleProtocol = a
print(protocolValue.simpleDescription)
A very simple class.  Now 100% adjusted.

// Uncomment to see the error.
// protocolValue.anotherProperty

変数protocolValueのランタイムタイプはSimpleClassが、コンパイラーはそれを指定されたタイプのExampleProtocolとして扱います。これは、プロトコルへの準拠に加えて、クラスが実装するメソッドやプロパティに誤ってアクセスすることができないことを意味します。

エラー処理

Errorプロトコルを採用する任意のタイプを使用してエラーを表します。

enum PrinterError: Error {
    case outOfPaper
    case noToner
    case onFire
}

throwを使用してエラーをthrowsし、 throwsを使用してエラーをthrowsできる関数をマークします。関数でエラーをスローすると、関数はすぐに戻り、関数を呼び出したコードがエラーを処理します。

func send(job: Int, toPrinter printerName: String) throws -> String {
    if printerName == "Never Has Toner" {
        throw PrinterError.noToner
    }
    return "Job sent"
}

エラーを処理する方法はいくつかあります。 1つの方法は、 do-catchを使用するdo-catchdoブロック内で、その前にtryを書き込むことにより、エラーをスローする可能性のあるコードをマークします。 catchブロック内では、別の名前を付けない限り、エラーには自動的にerrorという名前error付けられます。

do {
    let printerResponse = try send(job: 1040, toPrinter: "Bi Sheng")
    print(printerResponse)
} catch {
    print(error)
}
Job sent

// Experiment:
// Change the printer name to `"Never Has Toner"`, so that the `send(job:toPrinter:)` function
// throws an error.

特定のエラーを処理する複数のcatchブロックを提供できます。スイッチのcase後と同じように、 catch後にパターンを記述します。

do {
    let printerResponse = try send(job: 1440, toPrinter: "Gutenberg")
    print(printerResponse)
} catch PrinterError.onFire {
    print("I'll just put this over here, with the rest of the fire.")
} catch let printerError as PrinterError {
    print("Printer error: \(printerError).")
} catch {
    print(error)
}
Job sent

// Experiment:
// Add code to throw an error inside the `do` block.
// What kind of error do you need to throw so that the error is handled by the first `catch` block?
// What about the second and third blocks?

エラーを処理する別の方法は、 try?を使用するtry?結果をオプションに変換します。関数がエラーをスローした場合、特定のエラーは破棄され、結果はnilます。それ以外の場合、結果は関数が返した値を含むオプションです。

let printerSuccess = try? send(job: 1884, toPrinter: "Mergenthaler")
let printerFailure = try? send(job: 1885, toPrinter: "Never Has Toner")

deferを使用して、関数内の他のすべてのコードの後、関数が戻る直前に実行されるコードのブロックを記述します。関数がエラーをスローするかどうかに関係なく、コードが実行されます。異なる時間に実行する必要がある場合でも、 deferを使用してセットアップコードとクリーンアップコードをdeferて記述deferことができます。

var fridgeIsOpen = false
let fridgeContent = ["milk", "eggs", "leftovers"]

func fridgeContains(_ food: String) -> Bool {
    fridgeIsOpen = true
    defer {
        fridgeIsOpen = false
    }

    let result = fridgeContent.contains(food)
    return result
}
print(fridgeContains("banana"))
print(fridgeIsOpen)
false
false

ジェネリック

ジェネリック関数または型を作成するには、山括弧内に名前を記述します。

func makeArray<Item>(repeating item: Item, numberOfTimes: Int) -> [Item] {
    var result = [Item]()
    for _ in 0..<numberOfTimes {
        result.append(item)
    }
    return result
}
print(makeArray(repeating: "knock", numberOfTimes: 4))
["knock", "knock", "knock", "knock"]

関数とメソッドの一般的な形式、およびクラス、列挙、構造を作成できます。

// Reimplement the Swift standard library's optional type
enum OptionalValue<Wrapped> {
    case none
    case some(Wrapped)
}
var possibleInteger: OptionalValue<Int> = .none
possibleInteger = .some(100)
print(possibleInteger)
some(100)

タイプ名の後にwhereを使用してwhere要件のリストを指定します。たとえば、プロトコルを実装するためにタイプを要求する場合、2つのタイプが同じである必要がある場合、またはクラスが特定のスーパークラスを持つことを要求する場合です。

func anyCommonElements<T: Sequence, U: Sequence>(_ lhs: T, _ rhs: U) -> Bool
    where T.Element: Equatable, T.Element == U.Element
{
    for lhsItem in lhs {
        for rhsItem in rhs {
            if lhsItem == rhsItem {
                return true
            }
        }
    }
    return false
}
print(anyCommonElements([1, 2, 3], [3]))
true

<T: Equatable>を書くことは、 <T> ... where T: Equatableを書くことと同じ<T> ... where T: Equatable