The Swift Programming Languageのメモ(その10)
はじめに
これは、『The Swift Programming Language』を読んだ時の個人的なメモです。なので、引用はすべて『The Swift Programming Language』から。
iTunes - ブック - Apple Inc.「The Swift Programming Language」
https://itunes.apple.com/jp/book/swift-programming-language/id881256329?mt=11
その9はこちら。
The Swift Programming Languageのメモ(その9) - 開発メモ
http://seeku.hateblo.jp/entry/2014/06/13/191735
Protocols
- プロトコルはメソッドやプロパティや他の要素をまとめた、設計図みたいなものだよ
Protocol Syntax
- プロトコルの文法はこんな感じになるよ
protocol SomeProtocol { // protocol definition goes here }
- プロトコルをクラスに適用する時は、こんな感じになるよ
class SomeClass: SomeSuperclass, FirstProtocol, AnotherProtocol { // class definition goes here }
Property Requirements
プロトコルでプロパティを宣言した場合、実際の定義は値を格納するプロパティでも計算するプロパティでも良いよ
名前と型さえあってれば問題ないよ。getter/setterの有無は『get』と『set』で指定するよ
- 具体的にはこんな感じになるよ
protocol SomeProtocol { var mustBeSettable: Int { get set } var doesNotNeedToBeSettable: Int { get } }
Method Requirements
- プロトコルはインスタンスメソッドや型メソッドを要求できるよ
- メソッドや型メソッドは、通常のメソッド定義と同じように書くけど本文は無いよ
可変引数は使えるけど、デフォルト値は使えないよ
プロトコルで型メソッドを定義する場合、クラスで使う時は『class』を、構造体や列挙型で使う場合は『static』を前につけるよ
メモ: classとstaticを分ける必要があったのか?
Mutating Method Requirements
Protocols as Types
- プロトコルは型だよ。
Adding Protocol Conformance with an Extension
- エクステンションを使って、既存の型にプロトコルを適用させることも出来るよ
Declaring Protocol Adoption with an Extension
- すでに実装が揃ってる場合、プロトコルの適用を宣言するだけで良いよ
struct Hamster { var name: String func asText() -> String { return "A hamster named \(name)" } } extension Hamster: TextRepresentable {}
Protocol Inheritance
Protocol Composition
- プロトコルは合成できるよ
- 具体的にはこんな感じになるよ
protocol Named { var name: String { get } } protocol Aged { var age: Int { get } } struct Person: Named, Aged { var name: String var age: Int } func wishHappyBirthday(celebrator: protocol<Named, Aged>) { println("Happy birthday \(celebrator.name) - you're \(celebrator.age)!") } let birthdayPerson = Person(name: "Malcolm", age: 21) wishHappyBirthday(birthdayPerson) // prints "Happy birthday Malcolm - you're 21!
Checking for Protocol Conformance
プロトコルが適合してるかどうかは、型キャストで調べられるよ
NOTE: プロトコルが適合してるかどうか調べられるのは、プロトコルに『@objc』属性がついてる時だけだよ。この属性はObjective-Cのコードから使われる事を意味してるよ。Objective-Cのコードを使わない場合でも、プロトコルの適合を調べるためにはこの属性を付ける必要があるよ。
メモ: 本当にこの解釈で正しいのか? 要確認『@objc』がついたプロトコルは、クラスにだけ使えるよ
Optional Protocol Requirements
- プロトコルに必要な要素をオプション指定することも出来るよ
- オプション指定された要素は、定義しなくても良くなるよ
オプション指定する時は『@optional』を前につけるよ
NOTE: オプション指定は『@objc』属性を付けたプロトコルだけが使えるよ。
具体的にはこんな感じになるよ
@objc protocol CounterDataSource { @optional func incrementForCount(count: Int) -> Int @optional var fixedIncrement: Int { get } }
Generics
- ジェネリックは・・・ よくあるやつだよ
Generic Functions
- ジェネリックを使う関数はこんな感じになるよ
func swapTwoValues<T>(inout a: T, inout b: T) { let temporaryA = a a = b b = temporaryA }
Type Constraints
- ジェネリックで使う型を制限することも出来るよ
Type Constraint Syntax
- 型を制限する場合、こんな感じになるよ
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) { // function body goes here }
Associated Types
- プロトコルを定義する時、関連する型を宣言できると便利なことがあるよ
- 関連する型は、プロトコルを定義する時に仮に使われる名前になるよ
- プロトコルが適用される時に、実際の型と置き換わるよ
- 関連する型は『typealias』で指定するよ
Associated Types in Action
- 具体的にはこんな感じになるよ
protocol Container { typealias ItemType mutating func append(item: ItemType) var count: Int { get } subscript(i: Int) -> ItemType { get } }
メモ: プロトコルの時点で、ジェネリック風に書いたほうがわかりやすいのでは?
- このプロトコルを適用するとこんな感じになるよ
struct IntStack: Container { // original IntStack implementation var items = Int[]() mutating func push(item: Int) { items.append(item) } mutating func pop() -> Int { return items.removeLast() } // conformance to the Container protocol typealias ItemType = Int mutating func append(item: Int) { self.push(item) } var count: Int { return items.count } subscript(i: Int) -> Int { return items[i] } }
Swiftは自動的に『Int』がItemTypeとして使わてることを推測するよ
これを、ジェネリックで書くとこうなるよ
struct Stack<T>: Container { // original Stack<T> implementation var items = T[]() mutating func push(item: T) { items.append(item) } mutating func pop() -> T { return items.removeLast() } // conformance to the Container protocol mutating func append(item: T) { self.push(item) } var count: Int { return items.count } subscript(i: Int) -> T { return items[i] } }
Where Clauses
- 『where』に続けて条件を書くことで、ジェネリックで使う型を制限出来るよ
Advanced Operators
- Objective-Cに似たビット操作演算子があるよ
Swiftの演算はデフォルトではオーバーフローを許さないけど、オーバーフローを許す演算子もあるよ
通常の演算子に『&』を付けたものだよ。Swiftでは、自由に演算子を定義できるよ
前置演算子、二項演算子、後置演算子、合成代入演算子のどれでも自由に出来るよ。
Bitwise Operators
- ビット単位でのNOT/AND/OR/XORが出来るよ
よくあるやつだよ。
Bitwise Left and Right Shift Operators
- よくあるビットシフトだよ
Overflow Operators
- Swiftでは、演算結果がオーバーフローになるとエラーになるよ
Division by Zero
- オーバーフローを許す演算で0で割った場合、結果は0になるよ
『&/』でも『&%』でも同じだよ。
let x = 1 let y = x &/ 0 // y is equal to 0
Precedence and Associativity
優先度(Precedence)は演算子の優先順位を示すよ
優先度が高いものほど、先に計算されるよ。結合規則(Associativity)は同じ優先度の中で、どういうふうにグループ化されるかを示すよ
つまり、左の演算子と結合されるか、右の演算子と結合されるかを決めるよ。NOTE: Swiftの演算子は、CやObjective-Cに比べて優先度も結合規則もシンプルでわかりやすくなってるよ。逆に言うと、他の言語と全く同じじゃないよ。他の言語から既存のコードを移植する時は、注意が必要だよ。
Operator Functions
struct Vector2D { var x = 0.0, y = 0.0 } func + (left: Vector2D, right: Vector2D) -> Vector2D { return Vector2D(x: left.x + right.x, y: left.y + right.y) }
- 使う時はこんな感じになるよ
let vector = Vector2D(x: 3.0, y: 1.0) let anotherVector = Vector2D(x: 2.0, y: 4.0) let combinedVector = vector + anotherVector // combinedVector is a Vector2D instance with values of (5.0, 5.0)
Prefix and Postfix Operators
Compound Assignment Operators
func += (inout left: Vector2D, right: Vector2D) { left = left + right }
すでに『+』が定義してある場合、わざわざ『+=』を定義する必要はないよ
NOTE: 標準の代入演算子『=』はオーバーロード出来ないよ。オーバーロード出来るのは、合成代入演算子だけだよ。あと、三項演算子もオーバーロード出来ないよ。
Equivalence Operators
- 自作のクラスや構造体は、デフォルトでは比較演算子を持ってないよ
- Swiftには、『等しい』って事を勝手に推測することは出来ないんだよ
なぜなら、『等しい』の意味は、その型がどう使われるかで決まるからね
自作のクラスや構造体で比較ができるようにする場合、こんな感じになるよ
func == (left: Vector2D, right: Vector2D) -> Bool { return (left.x == right.x) && (left.y == right.y) } func != (left: Vector2D, right: Vector2D) -> Bool { return !(left == right) }
Custom Operators
- 全く新しい演算子を追加することも出来るよ
カスタム演算子には、以下の記号が使えるよ
『/ = - + * % < > ! & | ^ . ~.』新しい演算子は、グローバルレベルで『operator』を使って宣言するよ
宣言する時は、prefix/infix/postfixを使うよ。
operator prefix +++ {}
- 実際の定義はこんな感じになるよ
prefix func +++ (inout vector: Vector2D) -> Vector2D { vector += vector return vector }
Precedence and Associativity for Custom Infix Operators
- 新しく二項演算子を定義する時は、優先度と結合規則を指定できるよ
結合規則にはleft/right/noneのどれかを指定するよ
leftは左結合、rightは右結合、noneは同じ優先度の演算子と続けて書けないってことだよ。デフォルトだと優先度は100、結合規則はnoneだよ
とりあえずここまで。
(2014/06/14 作成)
(2014/09/25 修正)