LoginSignup
16
3

More than 5 years have passed since last update.

Swiftにおけるインスタンスの文字列表現について ~CustomStringConvertible~

Last updated at Posted at 2018-07-30

アプリ開発中のデバッグ方法としてprintデバッグはお手軽でついつい使ってしまいます。
print(_:)関数はAny型の引数をとるので、数字や文字列だけでなく構造体やクラスのインスタンスもそのまま渡すことができるので非常に便利です。

print(1)
print("hello")
print(SomeStruct())
print(SomeClass())

UIKitやSwift標準ライブラリで提供されているクラスのインスタンスをprintデバッグすると、インスタンスのプロパティの値などをいい感じに出力してくれますが、このとき出力される文字列はどのように決まるのでしょうか。

print(Date())  // 2018-07-30 10:44:35 +0000
print(NSError(domain: "Some domain", code: 1, userInfo: nil))  // Error Domain=Some domain Code=1 "(null)"

実はprint(_:)関数は、引数に与えられたオブジェクトの文字列表現(textual representation)を出力しています。
本記事では、オブジェクトの文字列表現に関係するCustomStringConvertibleプロトコルやStringのイニシャライザについて説明します。

CustomStringConvertible

クラスや構造体をCustomStringConvertibleプロトコルに準拠させることで、インスタンスの文字列表現を自由にカスタマイズすることができます。
CustomStringConvertibleプロトコルに準拠させるには、descriptionプロパティを実装します。

class SomeClass: CustomStringConvertible {
    var description: String { return "This is SomeClass" }
}

print(_:)関数は、インスタンスがCustomStringConvertibleプロトコルに準拠している場合にこのdescriptionプロパティを使用します。

let some = SomeClass()
print(some)  // This is SomeClass

String.init(describing:)イニシャライザ

ある型のインスタンスの文字列表現を得るにはString.init(describing:)を使用します。
得られる文字列が実際何になるかは、以下のように決まります。

  • インスタンスがTextOutputStreamableプロトコルに準拠していれば、instance.write(to: s)の結果を得る(sは空文字列)
  • インスタンスがCustomStringConvertibleプロトコルに準拠していれば、instance.descriptionの結果を得る
  • インスタンスがCustomDebugStringConvertibleプロトコルに準拠していれば、instance.debugDescriptionの結果を得る
  • 上記のいずれにも当てはまらない場合、文字列表現はSwift標準ライブラリによって提供される

プロトコルに準拠していないクラス、CustomStringConvertibleに準拠したクラス、CustomDebugStringConvertibleに準拠したクラス、CustomStringConvertibleとCustomDebugStringConvertibleの両方に準拠したクラスを比較してみます。

class SomeClass1 {}

class SomeClass2: CustomStringConvertible {
    var description: String { return "This is SomeClass2" }
}

class SomeClass3: CustomDebugStringConvertible {
    var debugDescription: String { return "debug: This is SomeClass3" }
}

class SomeClass4: CustomStringConvertible, CustomDebugStringConvertible {
    var description: String { return "This is SomeClass4" }
    var debugDescription: String { return "debug: This is SomeClass4" }
}
let some1 = String(describing: SomeClass1())  // App.SomeClass1
let some2 = String(describing: SomeClass2())  // This is SomeClass2
let some3 = String(describing: SomeClass3())  // debug: This is SomeClass3
let some4 = String(describing: SomeClass4())  // This is SomeClass4

SomeClass1の文字列表現は、Swift標準ライブラリによってアプリ名.クラス名という形で出力されています。
SomeClass2の文字列表現は、descriptionプロパティで提供される文字列です。
SomeClass3の文字列表現は、debugDescriptionプロパティで提供される文字列です。
SomeClass4の文字列表現は、descriptionプロパティで提供される文字列です。

まとめ

オブジェクトの文字列表現に関係するCustomStringConvertibleプロトコルとString.init(describing:)イニシャライザについて説明しました。
print(_:)関数はオブジェクトのdescriptionプロパティを出力していたんですね。

Swift標準ライブラリのリファレンスを追っていくと、多くのクラスや構造体がCustomStringConvertibleプロトコルに準拠していることがわかります。
リファレンスを読むときに気にしてみて下さい。

リファレンス

CustomStringConvertible
https://developer.apple.com/documentation/swift/customstringconvertible

String.init(describing:)
https://developer.apple.com/documentation/swift/string/2427941-init

16
3
1

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
16
3