Swiftの勉強中なので、調べたり試したりしたことを備忘的に書きます。
なお、実行環境はmacOS High Sierra (10.13.3)、Xcode 9.2 (9C40b)のiOS Playgroundです。
init(describing:)が返す文字列の決定ルール
SwiftのStringのイニシャライザに、以下のようなものがあります。
init<Subject>(describing instance: Subject)
こいつは任意の型Subject
の内容をパパッと文字列化するのに便利なイニシャライザのようです。
APIリファレンスによると、このイニシャライザによって生成される文字列表現は以下のようなルールによって決まるようです。
-
instance
がTextOutputStreamableプロトコルに準拠する場合、空文字列s
に対してinstance.write(to: s)
を呼び出すことで結果が得られる。 -
instance
がCustomStringConvertibleプロトコルに準拠する場合、結果はinstance.description
となる。 -
instance
がCustomDebugStringConvertibleプロトコルに準拠する場合、結果はinstance.debugDescription
になる。 - Swift 標準ライブラリによって自動的に結果が与えられる。
ルールは1、2、3、4の順に適用されるようです。以下、試してみました。
ルール適用順序の確認
どのプロトコルにも準拠しない場合
class Person {
var name: String
var age: Int
init (name: String, age: Int) {
self.name = name
self.age = age
}
}
let p = Person(name: "John", age: 34)
print(String(describing: p))
// 出力
// __lldb_expr_1.Person
ルール4が適用されるため、Swift標準ライブラリが生成した"__lldb_expr_1.Person"
という結果となります。
CustomDebugStringConvertibleに準拠した場合
class Person: CustomDebugStringConvertible {
var debugDescription: String {
return "DEBUG: Person(name: \(self.name), age: \(self.age))"
}
var name: String
var age: Int
init (name: String, age: Int) {
self.name = name
self.age = age
}
}
let p = Person(name: "John", age: 34)
print(String(describing: p))
// 出力
// DEBUG: Person(name: John, age: 34)
ルール3が適用されます。
CustomStringConvertibleにも準拠した場合
class Person: CustomDebugStringConvertible, CustomStringConvertible {
var description: String {
return "Person(name: \(self.name), age: \(self.age))"
}
var debugDescription: String {
return "DEBUG: Person(name: \(self.name), age: \(self.age))"
}
var name: String
var age: Int
init (name: String, age: Int) {
self.name = name
self.age = age
}
}
let p = Person(name: "John", age: 34)
print(String(describing: p))
// 出力
// Person(name: John, age: 34)
ルール2が優先されるため、description
プロパティの値が返されます。
TextOutputStreamableにも準拠する場合
class Person: CustomDebugStringConvertible, CustomStringConvertible, TextOutputStreamable
{
func write<Target>(to target: inout Target) where Target : TextOutputStream {
target.write("STREAMABLE: \(self.description)")
}
var description: String {
return "Person(name: \(self.name), age: \(self.age))"
}
var debugDescription: String {
return "DEBUG: Person(name: \(self.name), age: \(self.age))"
}
var name: String
var age: Int
init (name: String, age: Int) {
self.name = name
self.age = age
}
}
let p = Person(name: "John", age: 34)
print(String(describing: p))
// 出力
// STREAMABLE: Person(name: John, age: 34)
ルール1が優先されるため、write(to:)
の結果が返されます。
NSObjectについて
NSObjectはCustomStringConvertible、CustomDebugStringConvertibleに準拠しています。
description
プロパティとdebugDescription
プロパティが返す文字列は同じです。
let nso = NSObject()
print(String(describing: nso))
print(nso.description)
print(nso.debugDescription)
// 出力
// <NSObject: 0x60400001c7e0>
// <NSObject: 0x60400001c7e0>
// <NSObject: 0x60400001c7e0>
したがって、NSObject
を継承するクラスのインスタンスをString(describing)
に渡して、インスタンスの内容を出力させたい場合は以下のいずれかの方策をとることになるでしょう。
-
description
プロパティをオーバーライドする - TextOutputStreamableに準拠させる
description
プロパティのオーバーライド
class NSPerson : NSObject {
override var description: String {
return "Person(name: \(self.name), age: \(self.age))"
}
var name: String
var age: Int
init (name: String, age: Int) {
self.name = name
self.age = age
}
}
// 出力
// Person(name: Cathy, age: 26)
名前、年齢を出力させることができました。
TextOutputStreamable
に準拠させる
class NSPerson : NSObject, TextOutputStreamable {
func write<Target>(to target: inout Target) where Target : TextOutputStream {
target.write("Person(name: \(self.name), age: \(self.age))")
}
var name: String
var age: Int
init (name: String, age: Int) {
self.name = name
self.age = age
}
}
let nsp = NSPerson(name: "Cathy", age: 26)
print(String(describing: nsp))
// 出力
// Person(name: Cathy, age: 26)
名前、年齢を出力させることができました。
まとめ
-
String(describing:)
にインスタンスを渡すことで、そのインスタンスの情報を文字列で取得することができる。 -
String(describing:)
が返す文字列の決定ルールとして、渡されたインスタンスがTextOutputStreamable
、CustomStringConvertible
、CustomDebugStringConvertible
の順に、各プロトコルに準拠しているかチェックされる。 - 準拠しているプロトコルが見つかれば、そのプロトコルのメソッドやプロパティが返す文字列が返される。
- いずれのプロトコルにも準拠していない場合、Swift標準ライブラリが提供する文字列表現が返される。