Swiftに関するメモ その9 - Extensions


* [
Swiftに関するメモ - 一覧](http://qiita.com/takkyun/items/f34abf857deed808c610)

https://developer.apple.com/swift/ より

[The Swift Programming Language (iBooks Store)](https://itunes.apple.com/us/book/the-swift-programming-language/id881256329?mt=11)をざっくり目を通してつらつらと。

# 型の拡張 - Extensions

`extension` キーワードを利用することで既存のクラスや構造体に新しいメソッドを追加することができます。

* 算出型プロパティ (static な算出型プロパティ) の追加
* メソッド (インスタンメソッド・タイプメソッド) の追加・再定義
	* 添字アクセス (subscription) の追加・再定義
	* 初期化 (Initializer) の追加
* ネストされた型の追加・再定義
* プロトタイプへの追加適用

などを行うことができます。

Objective-C の「カテゴリー」に近い機能で、継承などの仕組みを介することなく、既存のクラスや構造体を文字通り拡張することができます。

基本形：

```swift
extension SomeClass {
  // SomeClass に対しての追加メソッドなどを定義
}

extension SomeClass : SomeProtocol {
  // SomeClass に対する拡張
  // このスコープの中では、SomeProtocol に準拠している必要がある？
}
```

Objective-C とは異なり、Swift では `Int` や `Double` などのプリミティブな型でも `extension` で拡張することができます。

Swift では `Int` なども実のところ構造体などで定義されているようですので、扱いとしてはクラスや構造体の拡張と同じになるようです。

## 算出型プロパティの追加

```swift
extension Double {
  var permil: Double {
    return self / 1000.0
  }
  var percent: Double {
    return self / 100.0
  }
}

let someValue = 100.permil // someValue は 0.1
```

`extension` で格納型プロパティ (stored property / インスタンス変数) やプロパティの監視 (`willSet` / `didSet`) を追加することはできないようです。

## メソッドの追加

```swift
extension Int {
  func times(callback: () -> ()) {
    if self > 0 {
      for i in 0..self {
        callback()
      }
    }
  }  
}

3.times { println("Hello!") } // Hello! Hello! Hello! と 3 回出力
(-3).times { println("Hello!") } // 何もしない
```

型メソッド (静的メソッド / クラスメソッド) も同様の書式で追加できます。

## イニシャライザ

`extension` で独自のイニシャライザ (`init`) を追加することができます。この際、きちんと全てのプロパティが初期化されるようにする必要があります (クラスなら「指定された (Designated)」`init` が呼ばれるようにする)。

## 添字アクセス・mutating なメソッド・ネストされた型など

添字アクセスなども基本的な記述はメソッドと同様です。

```swift
extension Int {
  // 添字アクセスのサンプル
  subscript(index: Int) -> Int {
    if index == 0 {
      return self
    }
    return self % index
  }

  // mutating なメソッドのサンプル
  //  = インスタンスのプロパティを変更するメソッド 
  mutating func square() {
    self = self * self
  }

  // ネストされた型のサンプル
  enum Kind {
    case Even, Odd
  }

  // ネストされた型を利用している算出型プロパティ
  var kind: Kind {
    if self % 2 == 0 {
      return .Even
    }
    return .Odd
  }
}

var someInt = 6

someInt.square() // someInt = 36 (<= 6 * 6)

someInt[16] // = 4 (<= 36 % 16)
// サンプルでは Int の添字の結果は modular として算出される

somInt.kind // = Int.Kind.Even (<= 36 % 2 == 0)
```

# Extension を利用する上での注意事項

Objective-C のカテゴリーでは、あるクラスにすでに定義されているメソッドを複数箇所で再定義することができます (ビルドエラーなどにはなりません) が、そのメソッドを呼び出した時の動作は「未定義」で、どのような動作が起きるかは保証されていません。

Swift でも同様な動作になるのか、少なくとも [The Swift Programming Language (iBooks Store)](https://itunes.apple.com/us/book/the-swift-programming-language/id881256329?mt=11) をざっと見た限りでは分かりませんでした。

試しに以下のようなコードを組んでみます。

```swift
import Foundation

extension NSObject {
  class func hash() -> Int {
    return 1
  }
}

extension NSObject {
  class func hash() -> Double {
    return 2.0
  }
}
```

`NSObject` に対して、クラスメソッド `hash()` の再定義を行っています。

このようなコードがあると、

```swift
let objHash = NSObject.hash();
```

のような記述はできません。

`hash()` の再定義して `Double` 型を返す同名メソッドを追加したので、`objHash` の型が指定されていないと、どちらの型を適用するのか分からない (曖昧な : ambiguous) ためです。

```swift
let objHash : Int = NSObject.hash();
```

はどうでしょう。

`Int` と明示しているので、 `objHash` は `1` になりそうですが……。

とりあえず、既存するメソッドを `extension` で「上書き」するのは、やめておいた方が良いようです。