2020/1/12 Swift エクステンション(extension)

エクステンション(Extensions)は、既存のクラス・構造体・列挙型・プロトコルに対して機能を追加できます。

Swiftのエクステンションで以下のようなことができます。

  • コンピューテドプロパティ(インスタンスプロパティとタイププロパティ)を追加
  • インスタンスメソッドとタイプメソッドを定義
  • イニシャライザを追加
  • サブスクリプとを定義
  • 既存の型の定義内に新しい型の定義・新しい型の使用
  • 既存の型をあるプロトコルに準拠させる

既存の型にストアドプロパティを追加することはできません。

既存の型にプロパティオブザーバを追加することはできません。

NOTE

エクステンションは新しい機能を型に追加しますが、既存の機能をオーバーライドすることはできません。

1.すでにある型にプロパティ・メソッドなどを追加する。

1-1.メソッドを追加する

extension 要素を追加したい型名{
    追加したい要素
}

sample1-2

extension String{
    func exStr(){
        print("この文字列は\(self.count)文字です")
    }
}

let str1="ghjjkkdddd"
str1.exStr()
この文字列は10文字です

sample1-2では、既にSwiftの標準ライブラリの用意されているString型にexStr()メソッドを追加しています。

1-2.コンピューテドプロパティを追加する。

 

2.プロトコルエクステンション

既存のプロトコルに対して、プロパティやメソッドの実装を追加する。(sample2-1)

sample2-1

protocol Books{
    var color:String{get}
    var numOfPage:Int{get}
}
//↓↓infoというコンピューテドプロパティの実装をエクステンション
//↓↓により追加している。
extension Books{
    var info:String{
        return "表紙の色:\(color)、ページ数:\(numOfPage)ページ"
    }
}

struct Comic:Books{
    let color:String
    var numOfPage:Int=100
}

let comic1=Comic(color:"blue",numOfPage:100)
print(comic1.info)

sample2-1ではBooksプロトコルに対してinfoというコンピューテドプロパティの実装をエクステンションを用いて追加しています。これがプロトコルエクステンション。Booksプロトコルに記述されているプロパティcolorとnumOfPageは、準拠する型の中でも定義する必要がありますが、エクステンションにより追加されたinfoは準拠する型の中で定義しなくてもよいです。準拠する型の中で定義しない場合、プロトコルエクステンションに記述した実装(7行目~11行目)が使用されます。

3.プロトコルへの準拠をエクステンションで記述する

例えば構造体について説明すると、ある構造体がプロトコルに準拠する基本的な記述方法としてはこちらのページが基本だが、エクステンションを使って準拠する方法もある。

extension そのプロトコルに準拠する型名:プロトコル名{
    準拠するために必要な要素の定義
}



エクステンションの構文

extensionキーワードを使用して以下のように定義する。

sample1-1

extension SomeType {
    // new functionality to add to SomeType goes here
}

既存の型をあるプロトコルに準拠させるためには、以下のように記述します。

sample1-2

extension SomeType: SomeProtocol, AnotherProtocol {
    // implementation of protocol requirements goes here
}

既存のジェネリック型のextensionについてはこちら。


コンピューテドプロパティのエクステンション

sample2-1

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
print("One inch is \(oneInch) meters")
// Prints "One inch is 0.0254 meters"
let threeFeet = 3.ft
print("Three feet is \(threeFeet) meters")
// Prints "Three feet is 0.914399970739201 meters"

上記のように数値リテラルに対してドット構文により、mmプロパティやftプロパティにアクセスすることができます。

上記サンプルでは1.0は「1.0メートル」を表すと考えています。


イニシャライザ

エクステンションは、既存の型に対してイニシャライザを追加できます。これにより既存の型のイニシャライザが、独自に定義した型を取り扱えるようにすることができます。あるいは既存の型のオリジナルの実装にはない追加的なイニシャライザを提供することができます。

クラスに対して新しいイニシャライザを追加することはできますが、指定イニシャライザ(designated initializer)とデイニシャライザを追加することはできません。指定イニシャライザとデイニシャライザは必ずオリジナルのクラスの実装内で定義されなければなりません。

全てのストアドプロパティにデフォルト値があり、独自のイニシャライザを持っていない値型にエクステンションを使ってイニシャライザを追加する場合、エクステンションのイニシャライザから、その値型のデフォルトイニシャライザ・メンバワイズイニシャライザを呼び出すことができます。

下のサンプルは長方形を表現するRect構造体を定義しています。二つの補助的な構造体SizeとPointも定義しています。Size,Pointの二つとも各プロパティにデフォルト値0.0が与えられています。

sample3-1

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()
}

let defaultRect = Rect()
let memberwiseRect = Rect(origin: Point(x: 2.0, y: 2.0),
   size: Size(width: 5.0, height: 5.0))

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)
    }
}

let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
                      size: Size(width: 3.0, height: 3.0))
// centerRect's origin is (2.5, 2.5) and its size is (3.0, 3.0)

print(centerRect.origin,centerRect.size)
//Point(x: 2.5, y: 2.5) Size(width: 3.0, height: 3.0)

 

Rect構造体の全てのプロパティにデフォルト値がありますので、Rect構造体にはデフォルトイニシャライザとメンバワイズイニシャライザが自動的に用意されます。これらのイニシャライザは新しいRect構造体を生成するために使われます。

 

エクステンションによりRect構造体に、上記の二つのイニシャライザ(デフォルトイニシャライザとメンバワイズイニシャライザ)とは別のイニシャライザを追加することができます。

この新しいイニシャライザは、引数として受け取った中心点の座標とサイズから適切な起点の座標を計算します。それから、新しいイニシャライザは、構造体に自動的に用意されたメンバワイズイニシャライザinit(origin:size:)を呼び出し、それぞれのプロパティに計算結果の起点座標とサイズをセットします。

 

 

 

 

参考

https://docs.swift.org/swift-book/LanguageGuide/Extensions.html

コメントを残す

メールアドレスが公開されることはありません。