Swift 6.0 beta 日本語化計画 : Swift 6.0 beta
Swift のツアー
Swift の機能と構文を調べます。
伝統では、新しい言語で最初のプログラムは、スクリーン上に "Hello,world!" という言葉を表示するものであるべきだと示唆しています。Swift では、これは単一の行で行うことができます。
- print("Hello, world!")
- // Prints "Hello,world!"
別の言語を知っている場合は、この構文に見覚えがあるはずですーSwift では、このコード行が完全なプログラムです。テキストの出力や文字列の処理などの機能のために別のライブラリをインポートする必要はありません。グローバルスコープで記述されたコードはプログラムのエントリポイントとして使用されるため、main( ) 関数は必要ありません。また、すべての分の最後にセミコロンを書く必要もありません。
このツアーでは、色々なプログラミングのタスクを実行する方法を示すことによって、Swift でコードを書き始めるのに十分な情報を提供します。何かわからない事があっても心配しないで下さい。このツアーで紹介するすべてはこの本の残りの部分で詳しく説明されています。
単純な値
let で定数を作成し、var で変数を作成します。定数の値はコンパイル時に知っている必要はありませんが、一度だけ、それに値を代入しなければなりません。これは、一度決定するが、多くの場所で使用する値に名前を付けて定数を使用できることを意味します。
- var   myVariable = 42
- myVariable = 50
- let  myConstant = 42
定数または変数は、それに代入したい値と同じ型を持たなければなりません。ただし、常に明示的に型を記述する必要はありません。定数や変数を作成して値を与えると、コンパイラはその型を推論します。上記の例では、コンパイラは、その初期値が整数であるため myVariable が整数であると推論します。
初期値が十分な情報を提供しない(または初期値が存在しない場合)、変数の後にコロンで区切って、型を書くことで指定して下さい。
- let  implicitInteger = 70
- let  implicitDouble = 70.0
- let  explicitDouble: Double = 70
実験: 明示的な Float 型で 4 の値を持つ定数を作成して下さい。
値は、別の型に暗黙的に変換されることは決してありません。別の型に値を変換する必要がある場合は、明示的に希望の型のインスタンスを作って下さい。
- let  label = "The width is "
- let  width = 94
- let  widthLabel = label + String(width)
実験: 最後の行から String への変換を削除してみてください。どのようなエラーが出ましたか?
文字列内に値を含むさらに簡単な方法があります。括弧内に値を書き、括弧の前にバックスラッシュ(\)を書きます。たとえば、次のように:
- let  apples = 3
- let  oranges = 5
- let  appleSummry = "I have \(apples) apples."
- let  fruitSummary = "I have \(apples + oranges) pieces of fruit."
実験: 文字列内に浮動小数点演算を含むように、また挨拶で誰かの名前が含まれるように \( ) を使用して下さい。
複数の行を取る文字列には 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.
"""
括弧([ ]) を使用して、配列や dictionary を作成し、括弧内に指標やキーを書き込むことによって、それらの要素にアクセスします。
- var fruits = ["strawberries", "limes", "tangerines"]
- fruits[1] = "grapes"
-
- var occupations = [
"Malcolm": "Captain",
"Kaylee": "Mechanic",
- ]
- occupations["Jayne"] = "Public Relations"
要素を追加すると配列は自動的に大きくなります。
- fruits.append("blueberries")
- print(fruits)
- // Prints "["strawberries", "grapes", "tangerines", "blueberries"]"
また、括弧を使用して、空の配列または辞書を記述します。配列の場合は []、辞書の場合は [:]と書きます。
- fruits = []
- occupations = [:]
空の配列または辞書を新しい変数、または型情報がない別の場所に割り当てる場合は、型を指定する必要があります。
- let emptyArray: [String] = []
- let emptyDictionary: [String: Float] = [:]
制御フロー
条件文を作るため、if と switch を使い、for-in,while,及び 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)
- // Prints "11"
if 文では、条件は ブール 式でなければならず、つまり、if score { ... } のようなコードはエラーであり、ゼロとの暗黙の比較ではありません。
代入の等号 (=) の後、または return の後に if または switch を記述して、条件に基づいて値を選択できます。
- let scoreDecoration = if teamScore > 10 {
"🎉"
- } else {
""
- }
- print("Score:", teamScore, scoreDecoration)
- // Prints "Score: 11 🎉"
if と let を一緒に使用すると、欠落している可能性のある値を処理できます。これらの値は optional として表されます。optional の値には、値が含まれるか、値が欠落していることを示す nil が含まれます。値を optional としてマークするには、値の型の後に疑問符 (?) を記述します。
- var optionalString: String? = "Hello"
- print(optionalString == nil)
- // Prints "false"
- var optionalName: String? = "John Appleseed"
- var greeting = "Hello!"
- if let name = optionalName {
greeting = "Hello, \(name)"
- }
実験:optionalName を変更して nil にします。何の挨拶をされましたか? optionalName が nil の場合、別の挨拶を設定するために else 節を追加して下さい。
optional の値が nil の場合、条件文は false であり、括弧内のコードはスキップされます。それ以外の場合、optional の値は開封され、コードブロック内の利用可能な開封値になり、これは let の後の定数に代入されます。
optional の値を処理するもう 1 つの方法は、?? 演算子を使用してデフォルト値を提供することです。optional の値が欠落している場合、デフォルト値が代わりに使用されます。
- let nickName: String? = nil
- let fullName: String = "John Appleseed"
- let informalGreeting = "Hi \(nickName ?? fullName)"
開封した同じ名前を使用して、より短いスペルを使用して値を開封できます。
- if let nickname {
print(
"Hey, \(
nickname)
")
- }
- // Doesn't print anything, because nickname is nil.
Switch は、どんな種類のデータでもサポートし、広範囲の比較操作をサポートし、それらが等しいかどうかのテストは、整数に限定されません。
- 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.")
- }
- // Prints "Is it a spicy red pepper?"
実験: デフォルトのケースを削除してみてください。どのようなエラーが出るでしょう?
定数へのパターンが一致した値を代入するパターンで let がどのように使用できるかに注目しましょう。
一致した switch case 内のコードを実行した後、プログラムは switch 文を終了します。実行は、次の case に進みませんので、明示的にそれぞれの case のコードの最後に switch を break する必要はありません。
各キー値のペアで使用する名前のペアを提供することにより、dictionary 内の項目を反復処理するために、 for-in を使用して下さい。dictionary は順不同のコレクションであり、そのキー値は任意の順序で反復されます。
- 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  (_, numbers)  in interestingNumbers {
for  number  in  numbers {
if number > largest {
largest =
number
}
}
- }
- print(largest)
- Prints "25"
実験:_ を変数名に置き換え、どの種類の数値が最大であったかを追跡してみましょう。
条件が変化するまで、コードのブロックを繰り返すには while を使用します。ループが少なくとも一度実行されることを保証する代わりに、ループの条件を最後にすることができます。
- var  n = 2
- while  n < 100 {
n *= 2
- }
- print(n)
- // Prints "128"
- var m  =  2
- repeat {
m *= 2
- } while m <  100
- print(m)
- // Prints "128"
実験: 条件を m < 100 から m < 0 に変更して、ループ条件がすでに false の場合に while と repeat-while の動作がどのように異なるかを確認しましょう。
指標の範囲を作るために ..< を使用して、ループで指標を維持することができます。
- var total = 0
- for i in 0..<4 {
total += i
- }
- print(total)
- // Prints "6"
..< を使って、上限の値を省略した範囲を作り、... を使って両方の値を含む範囲を作成します。
関数とクロージャ
関数を宣言するためには、func を使用してください。括弧内の引数のリストと、その名前に続けることで関数を呼び出します。-> を使って関数の戻り値の型からパラメータの名前と型を分離します。
- func greet(person: String, day: String) -> String {
return "Hello \(person), today is \(day)."
- }
- greet(person:"Bob", day: "Tuesday")
実験: day のパラメータを削除して、特別な今日の昼食の挨拶が含まれるようにパラメータを追加してみましょう。
デフォルトでは、関数は引数用のラベルとして、それらのパラメータ名を使用します。パラメータ名の前にカスタム引数のラベルを書くか、または引数のラベルを使用しないように _ を書きます。
- func greet(_ person: String, on day: String) -> String {
return "Hello \(person), today is \(day)."
- }
- greet("John", on: "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)
- // Prints "120"
- print(statistics.2)
- // Prints "120"
関数は入れ子にできます。入れ子にされた関数は、外部関数で宣言された変数にアクセスできます。入れ子にされた関数を使用して、長く複雑な関数のコードを整理できます。
- func returnFifteen( ) -> Int {
var y = 10
func add( ) {
y += 5
}
add( )
return y
- }
- returnFifteen( )
関数は最高級の型です。これは、関数がその値として別の関数を返せることを意味します。
- func  makeIncrementer( ) -> ((Int) -> Int) {
func  addOne(number: Int) -> Int {
return 1 + number
}
return addOne
- }
- var increment = makeIncrementer( )
- increment(7)
関数は、別の関数を、引数の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]
- hasAnyMatches(list: numbers, condition: lessThanTen)
関数は、実際にはクロージャ (後で呼び出すことができるコードのブロック) の特殊なケースです。クロージャのコードは、入れ子になった関数で既にこの例を見ていますが、実行されるときは別のスコープにクロージャがある場合でも、クロージャが作成されたスコープ内で使用可能な変数や関数のようなものにアクセスできます。中括弧({})を使用してコードを囲んで、名前のないクロージャを書くことができます。引数を分離し、本体から型を返すように in を使用して下さい。
- numbers.map({   (number: Int) -> Int in
let result = 3 * number
return result
- })
実験: すべての奇数でゼロを返すようにクロージャを書き換えてみましょう。
もっと簡潔にクロージャを書くためにいくつかのオプションがあります。クロージャの型が既に分かっている場合、デリゲートの呼び出し関数のように、そのパラメータ、その戻り値の型、または両方の型を省略できます。単一の文のクロージャは、暗黙のうちにそれらの唯一の文の値を返します。
- let mappedNumbers = numbers.map({ number in 3 * number })
- print(mappedNumbers)
- // Prints "[60, 57, 21, 36]"
名前の代わりに番号で、パラメータを参照することができ、このアプローチは、非常に短いクロージャで特に有用です。関数への最後の引数として渡されたクロージャは、括弧の直後に表示されることがあります。クロージャが関数の唯一の引数であるときは、括弧を完全に省略することができます。
- let sortedNumbers = numbers.sorted { $0 > $1 }
- print(sortedNumbers)
- // Prints "[20, 19, 12, 7]"
オブジェクトとクラス
クラスを作成するには、class の後にクラスの名前を続けて下さい。クラス内のプロパティの宣言は、クラスのコンテキスト内であることを除いては、定数または変数の宣言と同じように書かれます。同様に、メソッドおよび関数の宣言も、同じように書かれます。
- class Shape {
var numberOfSides = 0
func simpleDescription( ) -> String {
return "A shape with \(numberOfSides) sides."
}
- }
実験: let で定数のプロパティを追加して、引数を取る別のメソッドを追加してみましょう。
クラスの名前の後に括弧を書くことによって、クラスのインスタンスを作成します。プロパティとインスタンスのメソッドにアクセスするには、ドット構文を使用します。
- 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 を使用してください。
サブクラスは、コロンで区切られたクラス名の後に、それらのスーパークラスの名前を含みます。クラスは、標準的なルートクラスをサブクラス化する必要はなく、必要に応じてスーパークラスを省略したり含めたりできます。
スーパークラスの実装をオーバーライドするサブクラス上のメソッドは 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")
- test.area( )
- test.simpleDescription( )
実験: Circle と呼ばれる NamedShape の別のサブクラスを作成し、そのイニシャライザへの引数として半径と名前を取るものを作りましょう。Circle クラス上の area( ) と simpleDescription( ) メソッドを実装しましょう。
格納されている単純なプロパティに加えて、プロパティは getter と setter を持つことができます。
- 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)
- // Prints "9.3"
- triangle.perimeter = 9.9
- print(triangle.sideLength)
- // Prints "3.3000000000000003"
perimeter の setter では、新しい値は、暗黙の名前 newValue を持っています。set の後の括弧内に明示的な名前を提供できます。
EquilateralTriangle クラスのイニシャライザには、3つの異なる段階があることに注意してください:
- サブクラスが宣言したプロパティの値を設定します。
- スーパークラスのイニシャライザを呼び出します。
- スーパークラスで定義されたプロパティの値を変更します。メソッド、getter、または setter を使用するすべての追加の設定作業もこの時点で行うことができます。
プロパティを計算する必要がないが、新しい値を設定した前後に実行されるコードを提供する必要がある場合には、willSet と didSet を使用して下さい。あなたが提供するコードは、いつでもイニシャライザの外側で値が変更した時に実行されます。例えば、以下のクラスはその三角形の辺の長さが常にその正方形の一辺の長さと同じであることを保証します。
- 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)
- // Prints "10.0"
- print(triangleAndSquare.triangle.sideLength)
- // Prints "10.0"
- triangleAndSquare.square = Square(sideLength: 50, name: "larger square")
- print(triangleAndSquare.triangle.sideLength)
- // Prints "50.0"
optional の値を操作する時は、? をメソッド、プロパティ、およびサブスクリプトのような演算子の前に書くことが出来ます。もし ? の前の値が nil の時は、? の後のすべては無視され、式全体の値は nil になります。それ以外の場合は、optional の値は開封され、? の後のすべては開封された値に作用します。両方の場合とも、式全体の値は optional の値になります。
- let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
- let sideLength = optionalSquare?.sideLength
列挙型と構造体
列挙型を作成するためには 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
- let aceRawValue = ace.rawValue
実験: それらの生の値を比較することにより、2つの Rank 値を比較する関数を記述してみましょう。
デフォルトでは、Swift は生の値がゼロで開始し、1 ずつ増分しますが、明示的に値を指定することで、この動作を変更することができます。上記の例では、Ace は、明示的に 1 の生の値が与えられ、そして生の値の残りは、順に割り当てられます。また、列挙型の生の型として文字列や浮動小数点数も使用できます。列挙型の case の生の値にアクセスするために rawValue プロパティを使用します。
init?(rawValue:) イニシャライザを使用して、生の値から列挙型のインスタンスを作成します。生の値と一致する列挙型 case か、一致する Rank がない場合は nil を返します。
- if let convertedRank = Rank(rawValue: 3) {
let threeDescription = convertedRank.simpleDescription( )
- }
列挙型の case の値は、実際の値であり、それらの生の値を書き込むだけの他の方法ではありません。実際、意味のある生の値が存在しない case では、1つも提供する必要はありません。
- 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( )
実験: Suit に、スペードとクラブには"black"を返し、ハートとダイヤモンドには"red"を返す color( ) のメソッドを追加してみましょう。
上記の例で、列挙型の hearts の case が参照されている二つの方法に注目してください。hearts 定数に値を代入すると、その定数は指定された明示的な型を持っていないため、列挙型の case Suit.hearts は、そのフルネームで参照されます。switch の内部では、self の値はすでに suit であることが知られているので、列挙型の case は、省略形の .hearts によって参照されます。値の型が既に知られている時はいつでも省略形を使用できます。
列挙型に生の値がある場合、それらの値は宣言の一部として決定されます。つまり、特定の列挙型 case のすべてのインスタンスが常に同じ生の値を持つことを意味します。列挙型 case の別の選択肢は、case に関連した値を持つことです。これらの値は、インスタンスの作成時に決定され、列挙型 case の各インスタンスごとに異なる場合があります。関連した値は、列挙型 case のインスタンスの格納されたプロパティのように動作すると考えることができます。たとえば、サーバーから日の出と日没の時刻を要求する場合を考えてみましょう。サーバーは要求された情報で応答するか、何が悪かったかの説明で応答します。
- 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)")
- }
- // Prints "Sunrise is at 6:00 am and sunset is at 8:09 pm."
実験: ServerResponse および switch に第三の case を追加してみましょう。
日の出と日没の時間が、switch case に対する値に一致する一部として ServerResponse の値から抽出されることに注意してください。
struct を、構造体を作成するために使用してください。構造体はメソッドおよびイニシャライザを含めた、クラスと同じ多くの動作をサポートします。構造体とクラスの間の最も重要な違いの一つは、構造体はコードの中で渡された場合には常にコピーされますが、クラスは参照によって渡されるということです。
- 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( )
実験: rank と suit の各々の組み合わせの一枚のカードで、トランプの完全な一組を含む、配列を返す関数を書いてみましょう。
同時実行
async を使用して、非同期で実行される関数をマークします。
- func fetchUserID(from server: String) async -> Int {
if server == "primary" {
return 97
}
return 501
- }
非同期関数の呼び出しは、その前に await を記述してマークします。
- func fetchUsername(from server: String) async -> String {
let userID = await fetchUserID(from: server)
if userID == 501 {
return "John Appleseed"
}
return "Guest"
- }
async let を使用して非同期関数を呼び出し、他の非同期コードと並行して実行できるようにします。返される値を使用する場合は、await と記述します。
- func connectUser(to server: String) async {
async let userID = fetchUserID(from: server)
async let username = fetchUsername(from: server)
let greeting = await "Hello \(username), user ID \(userID)"
print(greeting)
- }
Task を使用して、return を待たずに同期コードから非同期関数を呼び出します。
- Task {
await connectUser(to: "primary")
- }
- // Prints "Hello Guest, user ID 97"
タスクグループを使用して同時実行コードを構造化します。
- let userIDs = await withTaskGroup(of: Int.self) { group in
for server in ["primary", "secondary", "development"] {
group.addTask {
return await fetchUserID(from: server)
}
}
var results: [Int] = []
for await result in group {
results.append(result)
}
return results
- }
アクター (actor) はクラスに似ていますが、異なる非同期関数が同時に同じアクターのインスタンスを安全に操作できるようにする点が異なります。
- actor ServerConnection {
var server: String = "primary"
private var activeUsers: [Int] = []
func connect( ) async -> Int {
let userID = await fetchUserID(from: server)
// ... communicate with server ...
activeUsers.append(userID)
return userID
}
- }
アクター上のメソッドを呼び出すとき、またはそのプロパティの 1 つにアクセスするときは、そのコードに await のマークを付けて、アクター上ですでに実行されている他のコードが終了するまで待機する必要がある可能性を示します。
- let server = ServerConnection( )
- let userID = await server.connect( )
プロトコルと拡張機能
プロトコルを宣言するためには 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( )
- b.adjust( )
- let bDescription = b.simpleDescription
実験: ExampleProtocol に別の要件を追加します。SimpleClass と SimpleStructure に対して、プロトコルにまだ準拠するように変更する必要があるのは何ですか?
構造体を変更するメソッドをマークするために SimpleStructure の宣言の中で mutating キーワードを使う事に注意してください。SimpleClass の宣言は、クラスのメソッドはいつでもクラスを変更できるため、mutating としてそのメソッドをマークする必要はありません。
新しいメソッドや計算されたプロパティのような、既存の型に機能を追加するのに extention を使用してください。あるいは、ライブラリやフレームワークからインポートした型にさえも、別の場所で宣言されている型にプロトコルへの準拠を追加するために extension を使用できます。
- extension Int: ExampleProtocol {
var simpleDescription: String {
return "The number \(self)"
}
mutating func adjust( ) {
self += 42
}
- }
- print(7.simpleDescription)
- // Prints "The number 7"
実験: absoluteValue プロパティを追加する Double 型の拡張機能を書いてみましょう。
異なる名前の型を持つようなプロトコル名を使用できます:例えば、異なる型を持つが、すべてが単一のプロトコルに準拠しているオブジェクトのコレクションを作成できます。型がボックス化されたプロトコル型である値を操作する場合、プロトコル定義外のメソッドは使用できません。
- let protocolValue: ExampleProtocol = a
- print(protocolValue.simpleDescription)
- // Prints "A very simple class. Now 100% adjusted."
- // print(protocolValue.anotherProperty) // Uncomment to see the error
変数 protocolValue は SimpleClass の実行時の型を持っているにもかかわらず、コンパイラは ExampleProtocol の与えられた型として扱います。これは、誤ってクラスがそのプロトコルの準拠性に加えて、クラスが実装するメソッドやプロパティにアクセスできないことを意味します。
エラー処理
Error プロトコルを採用している全ての型を使用してエラーを表す事ができます。
- enum PrinterError: Error {
case outOfPaper
case noToner
case onFire
- }
エラーをスローするため throw を使用して、エラーを throw することができる関数をマークするために throws を使用します。関数内でエラーをスローした場合、関数はすぐに返し、関数を呼び出したコードは、エラーを処理します。
- func send(job: Int, toPrinter printerName: String) throws -> String {
if printerName == "Never Has Toner" {
throw PrinterError.noToner
}
return "Job sent"
- }
エラーを処理するにはいくつかの方法があります。一つの方法が do-catch を使用することです。do ブロックの内部では、それの前に try を書くことによって、エラーを throw できるコードをマークします。それに別の名前を付けない限り、catch ブロックの内部では、エラーは自動的に error の名前を与えられます。
- do {
let printerResponse = try send(job: 1040, toPrinter: "Bi Sheng")
print(printerResponse)
- } catch {
print(error)
- }
- // Prints "Job sent"
実験:send(job:toPrinter:) 関数はエラーを throw するように、プリンターの名前を "Never Has Toner" に変えます。
特定のエラーを処理する複数の catch ブロックを提供できます。switch 内の 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)
- }
- // Prints "Job sent"
実験: do ブロック内に、エラーをスローするコードを追加してみましょう。どのような種類のエラーが最初の catch ブロックで処理されるようにスローする必要がありますか?第二および第三のブロックについてはどうですか?
エラーを処理する別の方法は、try? を使用し、optional に結果を変換する事です。関数がエラーをスローする場合、特定のエラーは破棄され、結果は nil となります。それ以外の場合、結果は関数が返した値を含む optional です。
- let printerSuccess = try? send(job: 1884, toPrinter: "Mergenthaler")
- let printerFailure = try? send(job: 1885, toPrinter: "Never Has Toner")
関数が戻る直前に、関数内の他のすべてのコードの後に実行されるコードのブロックを書くために 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
- }
- fridgeContains("banana")
- print("Found a banana")
- print(fridgeIsOpen)
- // Prints "false"
汎用(ジェネリック)
汎用の関数や型を作るために < > の山括弧の中に名前を書きます。
- func makeArray<Item>(repeating item: Item, numberOfTimes: Int) -> [Item] {
-
var result = [Item] = []
-
for _ in 0..<numberOfTimes {
-
result.append(item)
-