開発メモ

開発関係のメモをいろいろと。たぶん。

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

  • プロトコルで宣言したインスタンスメソッドから構造体や列挙型のプロパティを変更する場合、『mutating』を前につける必要があるよ

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

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

  • 合成代入演算子(『=』と他の演算子を組み合わせたやつ)も定義できるよ
  • 合成代入演算子では、左側の引数が常にinoutになるよ
func += (inout left: Vector2D, right: Vector2D) {
    left = left + right
}

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 修正)