しめ鯖日記

swift, iPhoneアプリ開発, ruby on rails等のTipsや入門記事書いてます

SwiftUIのsomeキーワードについて調べてみる

SwiftUIでViewを作る時、以下のようにsomeというキーワードが出てきます。
今回はこのキーワードについて調べてみました。

struct ContentView: View {
    var body: some View {
        Text("Hello, World!")
    }
}

調査にあたって以下の記事を参考にさせて頂きました。

Swift 5.1 に導入される Opaque Result Type とは何か - Qiita

someキーワードですが、実行時のパフォーマンス向上の為のキーワードになります。
具体的には「戻り値の型をプロトコルとして宣言するとメモリ使用量が増える」という悩みを解消してくれるものになります。

仮に下のようなクラスとプロトコルがあるとします。

protocol MyProtocol {
}

class MyClass: MyProtocol {
}

その時、下の2つのメソッドを比べるとMyProtocolを戻り値の型にしたほうがメモリ使用量が大きくなってしまいます。

// メモリ使用量が大きい
func myProtocol() -> MyProtocol { return MyClass() }
// メモリ使用量が小さい
func myProtocol() -> MyClass { return MyClass() }

それを解決するのがsomeキーワードです。
someを付ける事で、メソッドの実装によって戻り値の型が最適なものになります。

// メモリ使用量が大きい
func myProtocol() -> MyProtocol { return MyClass() }
// メモリ使用量が小さい
func myProtocol() -> some MyProtocol { return MyClass() }

実際に比較してみた結果は下のとおりです。
1,000,000個のインスタンスを作って、メモリ使用量を比較してみました。

結果を見るとsome MyProtocolMyClassとほぼパフォーマンスを出していることがわかります。

戻り値の型 1回目の測定結果 2回目の測定結果 3回目の測定結果 平均
MyClass 28.9mb 27.2mb 31.1mb 29.1mb
MyProtocol 48.7mb 58.3mb 61.4mb 56.2mb
some MyProtocol 28mb 32.4mb 31.6mb 30.7mb

100万回個のインスタンス生成の実装は以下の通りです。

let results = (0...1000000).map { _ in
    myProtocol()
}

メモリ使用量はXcodeのMemory reportのHighの値を使用しました。

f:id:llcc:20200127154802p:plain

以下のように実行速度についても調べたのですが、こちらは大きな差はありませんでした。

let d = Date()
let results = (0...1000000).map { _ in
    myProtocol()
}
print(Date().timeIntervalSince1970 - d.timeIntervalSince1970)

someキーワードはクラスにも使えるのですが、当然ながらsomeがあってもなくてもメモリ使用量に差は見えませんでした。

func myProtocol() -> some MyClass { return MyClass() }

余談ですがSwiftUIのViewからsomeを外すとProtocol 'View' can only be used as a generic constraint because it has Self or associated type requirementsというエラーになります。
これはViewが、associatedtypeを使っているためです。

struct ContentView: View {
    var body: View {
        Text("Hello, World!")
    }
}

someは引数には使うことができません。
引数はジェネリクスで対処する必要があります。

func myProtocol(myClass: some MyProtocol) { } // → x
func myProtocol<A: MyProtocol>(myClass: A) { } // → o