Help us understand the problem. What is going on with this article?

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

More than 5 years have passed since last update.

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 では IntDouble などのプリミティブな型でも 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 と明示しているので、 objHash1 になりそうですが……。

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

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした