はじめに
この記事はSwiftのObjectIdentifierについての記事です
ObjectIdentifierとは
ObjectIdentifierとはクラスまたはメタタイプの一意のidを表す型です。 structやenum, 関数、タプルに対してはObjectIdentifierを生成できなく、classやactorに対してObjectIdentifierを生成できます。
// --- Structに対してはObjectIdentifierを生成できない ---
struct S {}
let s = S()
let num: Int = 0
ObjectIdentifier(s) // Error: Argument type 'S' expected to be an instance of a class or class-constrained type
ObjectIdentifier(num) // Error: Argument type 'Int' expected to be an instance of a class or class-constrained type
// --- Classに対してはObjectIdentifierを生成できる ---
class B {
var value: Int
init(value: Int) {
self.value = value
}
}
actor A {}
let b = B(value: 0)
let a = A()
ObjectIdentifier(b)
ObjectIdentifier(a)
structに対してObjectIdentifierを生成しようとするとエラーになる |
---|
ObjectIdentifierによる比較は参照が同じという意味になるので===
と同じ意味になる認識です。
let a = B()
let b = B()
a === b // false
ObjectIdentifier(a) == ObjectIdentifier(b) // false
// 参照が同じならtrue
a === a // true
ObjectIdentifier(a) == ObjectIdentifier(a) // true
IdentifiableとObjectIdentifier
個人的にObjectIdentifierの存在を知ったきっかけがIdentifiableというプロトコルでした。
structに対してidentifiableを適合させるためにid
というプロパティを持たせる必要がありますが、コード補完を使うとid: ObjectIdentifier
というスニペットが出てきます。
Identifiableを適合させるためにid というプロパティを持たせる必要がある |
---|
しかし、実際はid: String
のようにObjectIdentifierではなくString型をidの型とすることもできます。
インターフェースを見ると、idの型はHashableでありclass-constrained typeである場合(AnyObjectを継承している場合)についてprotocol extensionが提供され、ObjectIdentifierでidのデフォルト実装が提供されていました。(参照)
/// `Identifiable` provides a default implementation for class types (using
/// `ObjectIdentifier`), which is only guaranteed to remain unique for the
/// lifetime of an object. If an object has a stronger notion of identity, it
/// may be appropriate to provide a custom implementation.
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
public protocol Identifiable<ID> {
/// A type representing the stable identity of the entity associated with
/// an instance.
associatedtype ID : Hashable
/// The stable identity of the entity associated with this instance.
var id: Self.ID { get }
}
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
extension Identifiable where Self : AnyObject {
/// The stable identity of the entity associated with this instance.
public var id: ObjectIdentifier { get }
}
自分の疑問は、Identifiableのidの型はObjectIdentifierじゃないといけないという認識が間違っていて、実際はHashableであれば良く、class-constrained typeである場合についてprotocol extensionがIdentifiableに準拠する実装が自動で提供されているということでした。 なのでclass/actorの場合はIdentifiableに準拠する際にid型を指定する必要はないということでした。(当然と言えば当然ですが)
struct S: Identifiable {
var id: String
}
class C: Identifiable {} // 追加実装不要で準拠できる
まとめ
- ObjectIdentifierはclass/actorに対してのみ生成できる
- Identifiableのidの型はObjectIdentifierではなくHashableであれば良い
- class/actorの場合はIdentifiableに準拠する際にid型を指定する必要はない
個人的にはIdentifiableのコード補完はid: ObjectIdentifier
ではなくid: some Hashable
でも良いと感じたのでどういう仕組みでコード補完が動いているのか覗いてみようと思ったのですが、swiftリポジトリに実装がないかもしれないので、そこから調べてみることになりそうです。