Int.init(ObjectIdentifier) and UInt.init(ObjectIdentifier) should have a bitPattern: label
01 何が問題だったのか
ObjectIdentifier は、クラスインスタンスやメタタイプを一意に識別するための値型で、内部的にはそのインスタンスへの参照を生ポインタとして保持しています。
Swift 3.0 以前の標準ライブラリには、ObjectIdentifier を Int や UInt に変換するための以下のようなイニシャライザが用意されていました。
extension UInt {
/// Create a `UInt` that captures the full value of `objectID`.
public init(_ objectID: ObjectIdentifier) {
self.init(Builtin.ptrtoint_Word(objectID._value))
}
}
extension Int {
/// Create an `Int` that captures the full value of `objectID`.
public init(_ objectID: ObjectIdentifier) {
self.init(bitPattern: UInt(objectID))
}
}
これらのイニシャライザには引数ラベルがなく、呼び出し側では次のように書いていました。
public func <(lhs: ObjectIdentifier, rhs: ObjectIdentifier) -> Bool {
return UInt(lhs) < UInt(rhs)
}
この書き方には次のような問題があります。
- 呼び出し箇所を見ても、ポインタの値をそのままビットパターンとして解釈して整数化しているのか、それとも何らかの数値的な意味での変換が行われているのかが一見してわかりません。
UInt(bitPattern: UnsafePointer<Void>(value))のような、ポインタ系の値から整数への変換を表す他の API ではbitPattern:ラベルが使われており、整合性が取れていません。
ObjectIdentifier から Int / UInt への変換は、実態としてはポインタのビット列をそのまま整数として取り出す操作です。その意図を呼び出し箇所から読み取れるようにする必要がありました。
02 どのように解決されるのか
Int と UInt が ObjectIdentifier を受け取るイニシャライザに、bitPattern: 引数ラベルを追加します。これにより、呼び出し箇所でポインタ値をビットパターンとして解釈していることが明示されます。
extension UInt {
/// Create a `UInt` that captures the full value of `objectID`.
public init(bitPattern objectID: ObjectIdentifier) {
self.init(Builtin.ptrtoint_Word(objectID._value))
}
}
extension Int {
/// Create an `Int` that captures the full value of `objectID`.
public init(bitPattern objectID: ObjectIdentifier) {
self.init(bitPattern: UInt(objectID))
}
}
呼び出し側は次のように bitPattern: を付けて書くことになります。
public func <(lhs: ObjectIdentifier, rhs: ObjectIdentifier) -> Bool {
return UInt(bitPattern: lhs) < UInt(bitPattern: rhs)
}
この形は、UnsafePointer から UInt への変換(UInt(bitPattern: UnsafePointer<Void>(value)))など、既存のポインタ系 API と一貫したスタイルになっています。
既存コードへの影響としては、ラベルなしで書かれていた呼び出しは修正が必要になりますが、従来のラベルなし API は unavailable としてマークされるため、コンパイラが修正候補を提示してくれます。