LoginSignup
9
8

More than 3 years have passed since last update.

【Swift】大文字のSelfまとめ

Last updated at Posted at 2021-03-29

はじめに

小文字のselfはよく使いますが、大文字のSelfというのもあります。
大文字のSelfについてまとめてみました。

型の内部で型自身へのアクセス

以下のように、Self.valueで構造体Aに紐付いたvalueにアクセスすることができます。

struct A {
    static let value = 10
    func printValue() {
        print(Self.value)
    }
}

ちなみに、Selfを書かずに直接スタティックプロパティ(value)にアクセスしようとすると
Static member 'value' cannot be used on instance of type 'A'
このようなエラーが出てきてしまいます。

スタティックメソッドでも同様です。

struct B {
    var name: String
    var age: Int
    static func isOld(_ x: Int) -> Bool {
        return x > 60
    }
    func ageFunc() {
        print(Self.isOld(age))
    }
}

ちなみに、Selfを構造体名のBと書き換えても同じことです。ここではSelf == Bですね。

プロトコルの定義での利用

プロトコルはそれに準拠させた型の振る舞いを事前に宣言しておくことができますが、プロトコル自身はどの型で準拠されるのかは知りません。例えるなら、車の大きさ、色、ハンドルの位置などは製造の段階で決める事ができますが、製造の段階では一体この車はどんな人が乗るのかは知りません。利用者(型)が購入(準拠)して初めてどんな人が乗るのか(プロトコル自身がどの型で準拠されるのか)を知る事ができます。
しかし、その型と同じ型のデータを返すメソッドや、同じ型同士を比較したい時もあります。その時にSelfキーワードを使います。

注意点として、前項ではその定義を含む型自身をSelfと置いていましたが、本項では少し扱い方が異なります。つまり、プロトコル定義内でSelfを用いてもプロトコルがSelfというわけではありません。ここでのSelfとは、プロトコルを採用した具体的な型 == Selfです。

protocol C {
    associatedtype Element
    var x: Element { get }
    var y: Element { get }
    func replace() -> Self
    static func +(lhs: Self, rhs: Self) -> Self
}

このように、xとyを入れ替えるreplaceメソッド、左辺の右辺を加算するスタティックメソッドをprotocol Cで宣言しています。replaceメソッドの戻り値にSelfを指定しています。この時点ではどのような型に準拠するのかわからないためです。スタティックメソッド+も同様です。
構造体Dに準拠させてみましょう。

struct D: C {
    typealias Element = Int
    var x: Int
    var y: Int
    func replace() -> D {
        return D(x: y, y: x)
    }
    static func +(lhs: D, rhs: D) -> D {
        return D(x: lhs.x + rhs.x, y: lhs.y + rhs.y)
    }
}

このように、Selfのところが具体的なかたDに変わったのがわかります。このプロトコルCは他の型にも紐付く事ができるようにするため、できるだけ具体的な型はプロトコル宣言時に書きたくないためです。(associatedtypeもそのため)

クラス内での利用

プロトコルとは違い、メソッドの返り値の型としてのみ扱う事ができます。

class E {
    func someFunc() -> Self {
        return self
    }
}

エクステンションでの利用

前述の通り、プロトコルを準拠する型をSelfで参照できますが、一定の条件を満たす型のみプロトコルエクステンションを有効にしたいこともあります。
そのような条件を記述するときにSelfでその型を表す事ができます。

protocol SomeProtocol { }

class SomeClass { }

extension SomeProtocol where Self: SomeClass {
    func someFunc() {
        print(type(of: self))
    }
}

class SomeSubClass: SomeClass, SomeProtocol { }

let someSubClass = SomeSubClass()
someSubClass.someFunc()

この例では、SomeProtocolを準拠する型が条件Self: SomeClassつまり、SomeClassを継承している場合のみsomeFunc()を利用する事ができます。
以下のように、SomeProtocolに準拠していてもSomeSubClass2SomeClassを継承していない(条件を満たしていない)ため、someFunc()にアクセスしようとするとエラーが出てきてしまいます。

class SomeSubClass2: SomeProtocol { }

let someSubClass2 = SomeSubClass2()
someSubClass2.someFunc()

エラー
Referencing instance method 'someFunc()' on 'SomeProtocol' requires that 'SomeSubClass2' inherit from 'SomeClass'

おわりに

selfと違ってSelfはややこしいですね。

9
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
8