我需要保留Swift元类型的集合并编写一个函数,该函数将检查给定对象是否是其中之一的实例。我可以在Java中轻松做到这一点:
Class c = x.getClass(); c.isInstance(someObj)
但是,我不知道如何在Swift中做到这一点:
var isInt = 7 is Int.Type // compiles let x = Int.self var isInt = 7 is x // compiler error - Use of undeclared type 'x'
这甚至可以在Swift中完成吗?
不幸的是,您目前只能对is运算符使用命名类型,还不能对其使用任意的元类型值(尽管实际上 应该 可以使用IMO )。
is
假设您可以控制要与之进行比较的元类型的创建,则达到相同结果的一种解决方案是创建一个包装器类型,该包装器类型具有一个初始化器,该初始化器存储一个is对通用占位符执行检查的闭包:
struct AnyType { let base: Any.Type private let _canCast: (Any) -> Bool /// Creates a new AnyType wrapper from a given metatype. /// The passed metatype's value **must** match its static value, /// i.e `T.self == base`. init<T>(_ base: T.Type) { precondition(T.self == base, """ The static value \(T.self) and dynamic value \(base) of the passed \ metatype do not match """) self.base = T.self self._canCast = { $0 is T } } func canCast<T>(_ x: T) -> Bool { return _canCast(x) } }
protocol P {} class C : P {} class D : C {} let types = [ AnyType(P.self), AnyType(C.self), AnyType(D.self), AnyType(String.self) ] for type in types { print("C instance can be typed as \(type.base): \(type.canCast(C()))") print("D instance can be typed as \(type.base): \(type.canCast(D()))") } // C instance can be typed as P: true // D instance can be typed as P: true // C instance can be typed as C: true // D instance can be typed as C: true // C instance can be typed as D: false // D instance can be typed as D: true // C instance can be typed as String: false // D instance can be typed as String: false
这种方法的唯一局限性在于,鉴于我们正在执行is检查T.self,因此必须强制执行T.self == base。例如,我们不能接受AnyType(D.self as C.Type),因为那么T.self将C.self同时base会D.self。
T.self
T.self == base
AnyType(D.self as C.Type)
C.self
base
D.self
但是,在您的情况下这应该不成问题,因为我们只是AnyType从编译时已知的元类型构造而来。
AnyType
但是,如果您无法控制元类型的创建(即您从API获得元类型),那么您在使用元数据时所受的限制就更大了。
如@adev所说,您可以使用type(of:)来获取给定实例的动态元类型,并使用==运算符确定两个元类型是否等效。但是,这种方法的一个问题是它同时忽略了类的层次结构和协议,因为子类型的元类型不会与父类型的元类型进行比较。
type(of:)
==
关于类的一种解决方案是使用Mirror,如本问答中所示:
Mirror
/// Returns `true` iff the given value can be typed as the given /// **concrete** metatype value, `false` otherwise. func canCast(_ x: Any, toConcreteType destType: Any.Type) -> Bool { return sequence( first: Mirror(reflecting: x), next: { $0.superclassMirror } ) .contains { $0.subjectType == destType } } class C {} class D : C {} print(canCast(D(), toConcreteType: C.self)) // true print(canCast(C(), toConcreteType: C.self)) // true print(canCast(C(), toConcreteType: D.self)) // false print(canCast(7, toConcreteType: Int.self)) // true print(canCast(7, toConcreteType: String.self)) // false
我们正在使用sequence(first:next:)从动态类型x到它可能具有的任何超类元类型来创建元类型序列。
sequence(first:next:)
x
但是,此方法仍不适用于协议。希望该语言的未来版本将提供更丰富的反射API,使您能够比较两个元类型值之间的关系。
然而,考虑到能够使用上面的知识Mirror,我们可以用它来解除上述限制T.self == base从我们的AnyType包装由单独处理类元类型:
struct AnyType { let base: Any.Type private let _canCast: (Any) -> Bool /// Creates a new AnyType wrapper from a given metatype. init<T>(_ base: T.Type) { self.base = base // handle class metatypes separately in order to allow T.self != base. if base is AnyClass { self._canCast = { x in sequence( first: Mirror(reflecting: x), next: { $0.superclassMirror } ) .contains { $0.subjectType == base } } } else { // sanity check – this should never be triggered, // as we handle the case where base is a class metatype. precondition(T.self == base, """ The static value \(T.self) and dynamic value \(base) of the passed \ metatype do not match """) self._canCast = { $0 is T } } } func canCast<T>(_ x: T) -> Bool { return _canCast(x) } } print(AnyType(D.self as C.Type).canCast(D())) // true
其中的情况下T.self是一个类元类型应该是其中唯一的情况下T.self != base,与协议,当T是一些协议P,T.Type是P.Protocol,它是协议本身的类型。目前,此类型只能保存valueP.self。
T.self != base
T
P
T.Type
P.Protocol
P.self