https://developer.apple.com/swift/ より
The Swift Programming Language (iBooks Store)をざっくり目を通してつらつらと。
型の拡張 - Extensions
extension
キーワードを利用することで既存のクラスや構造体に新しいメソッドを追加することができます。
- 算出型プロパティ (static な算出型プロパティ) の追加
- メソッド (インスタンメソッド・タイプメソッド) の追加・再定義
- 添字アクセス (subscription) の追加・再定義
- 初期化 (Initializer) の追加
- ネストされた型の追加・再定義
- プロトタイプへの追加適用
などを行うことができます。
Objective-C の「カテゴリー」に近い機能で、継承などの仕組みを介することなく、既存のクラスや構造体を文字通り拡張することができます。
基本形:
extension SomeClass {
// SomeClass に対しての追加メソッドなどを定義
}
extension SomeClass : SomeProtocol {
// SomeClass に対する拡張
// このスコープの中では、SomeProtocol に準拠している必要がある?
}
Objective-C とは異なり、Swift では Int
や Double
などのプリミティブな型でも extension
で拡張することができます。
Swift では Int
なども実のところ構造体などで定義されているようですので、扱いとしてはクラスや構造体の拡張と同じになるようです。
算出型プロパティの追加
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
) を追加することはできないようです。
メソッドの追加
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 なメソッド・ネストされた型など
添字アクセスなども基本的な記述はメソッドと同様です。
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) をざっと見た限りでは分かりませんでした。
試しに以下のようなコードを組んでみます。
import Foundation
extension NSObject {
class func hash() -> Int {
return 1
}
}
extension NSObject {
class func hash() -> Double {
return 2.0
}
}
NSObject
に対して、クラスメソッド hash()
の再定義を行っています。
このようなコードがあると、
let objHash = NSObject.hash();
のような記述はできません。
hash()
の再定義して Double
型を返す同名メソッドを追加したので、objHash
の型が指定されていないと、どちらの型を適用するのか分からない (曖昧な : ambiguous) ためです。
let objHash : Int = NSObject.hash();
はどうでしょう。
Int
と明示しているので、 objHash
は 1
になりそうですが……。
とりあえず、既存するメソッドを extension
で「上書き」するのは、やめておいた方が良いようです。