Menu

Category

Archive

logo


The Swift Programming Language ~ Extensions

2014-06-28 15:00:00 +0900
  • このエントリーをはてなブックマークに追加

iBooks にある “The Swift Programming Language” の勉強メモ。Objective-C と C を普段書いている自分から、ちょっと馴染みがないものを特にまとめておきます。目次は こちら

今回は、Extensions に関して。

Extensions

Extensions は、既存のクラス・構造体・Enumeration に対して新しい機能を追加します。これは Objective-C のカテゴリのようなものです。

1. Extensions Syntax

Extensions は、extension キーワードを使用して宣言されます。また、既存のタイプがプロトコルに準拠するように拡大することもできます。

1
2
3
4
5
6
extension SomeType {
    // new functionality to add to SomeType goes here
}
extension SomeType: SomeProtocol, AnotherProtocol {
    // implementation of protocol requirements goes here
}

2. Computed プロパティ

Extension は、Computed インスタンスプロパティと Computed タイププロパティを追加することができます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
extension Double {
    var km: Double { return self * 1_000.0 }
    var m: Double { return self }
    var cm: Double { return self / 100.0 }
    var mm: Double { return self / 1_000.0 }
    var ft: Double { return self / 3.28084 }
}
let oneInch = 25.4.mm
println("One inch is \(oneInch) meters")
// prints "One inch is 0.0254 meters"
let threeFeet = 3.ft
println("Three feet is \(threeFeet) meters")
// prints "Three feet is 0.914399970739201 meters"

let aMarathon = 42.km + 195.m
println("A marathon is \(aMarathon) meters long")
// prints "A marathon is 42195.0 meters long"

この例では、Swift の既存 Double タイプに対して、5 つの Computed インスタンスプロパティを追加しています。

3. Initializer

Extension は新しい Initializer も追加することができます。新しく Initializer を追加することで、既存の実装にはない新しい Initializer を使用したり、独自のカスタムタイプを引数として使用したりと便利にコーディングできます。しかし、Designvated Initializer と Deinitializer は追加することができません。これらは、常に元のタイプの実装の中になければなりません。

構造体や Enumeration に新しく Initializer を追加する際、もしそのタイプが全ての Stored プロパティに初期値を持っていれば、このデフォルト Initializer と Memberwise Initioaliszer を Extension の中で呼ぶことができます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
struct Size {
    var width = 0.0, height = 0.0
}
struct Point {
    var x = 0.0, y = 0.0
}
struct Rect {
    var origin = Point()
    var size = Size()
}

extension Rect {
    init(center: Point, size: Size) {
        let originX = center.x - (size.width / 2)
        let originY = center.y - (size.height / 2)
        self.init(origin: Point(x: originX, y: originY), size: size)
    }
}

この Rect 構造体は、デフォルト Initializer (init()) と Memberwise Initializer を使用することができます。全ての Stored プロパティに初期値が設定されているからです。そして、Extension の中で、この Memberwise Initializer を使用しています。

4. メソッド

Extension は、新しいインスタンスメソッドとタイプメソッドを追加することができます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
extension Int {
    func repetitions(task: () -> ()) {
        for i in 0..self {
            task()
        }
    }
}
3.repetitions({
    println("Hello!")
    })
// Hello!
// Hello!
// Hello!

// Using trailing closure syntax
3.repetitions {
    println("Goodbye!")
}
// Goodbye!
// Goodbye!
// Goodbye!

repetitions は 1 つ引数を持っています。これは、引数を持たず、返り値も持たない関数です。

Extension により追加されたインスタンスメソッドは、インスタンス自体に変更を加えることができます。構造体・Enumeration のメソッドが self やそのプロパティを修正する場合には、mutating キーワードを使用しなければなりません。

1
2
3
4
5
6
7
8
extension Int {
    mutating func square() {
        self = self * self
    }
}
var someInt = 3
someInt.square()
// someInt is now 9

5. Subscripts

Extension は、サブスクリプトを追加することができます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
extension Int {
    subscript(digitIndex: Int) -> Int {
        var decimalBase = 1
            for _ in 1...digitIndex {
                decimalBase *= 10
            }
            return (self / decimalBase) % 10
    }
}
746381295[0]
// returns 5
746381295[1]
// returns 9
746381295[2]
// returns 2
746381295[8]
// returns 7
746381295[9]
// returns 0, as if you had requested:
0746381295[9]

この例は、Swift の Int タイプに対して、指定された桁の数字を返すサブスクリプトを追加しています。

6. Nested Types

Extension は、Nested Type を追加することもできます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
extension Character {
    enum Kind {
        case Vowel, Consonant, Other
    }
    var kind: Kind {
    switch String(self).lowercaseString {
    case "a", "e", "i", "o", "u":
        return .Vowel
    case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m",
    "n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z":
        return .Consonant
    default:
        return .Other
        }
    }
}

この例は、Character タイプに対して Enumeration を追加しています。これにより、ある文字がどのような種類のものかを簡単に確認しています。

例えばこのように使用可能です。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
func printLetterKinds(word: String) {
    println("'\(word)' is made up of the following kinds of letters:")
    for character in word {
        switch character.kind {
        case .Vowel:
            print("vowel ")
        case .Consonant:
            print("consonant ")
        case .Other:
            print("other ")
        }
    }
    print("\n")
}
printLetterKinds("Hello")
// 'Hello' is made up of the following kinds of letters:
// consonant vowel consonant consonant vowel

character.kind は、すでに Character.Kind として分かっています。このため、switch 文の中では、単に .Vowel、.Consonant、.Other と書けます。Character.Kind.Vowel と書く必要はありません。