アプリ開発中のデバッグ方法として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