检查Swift对象是否是给定元类型的实例

时间:2017-07-02 18:48:54

标签: swift types metatype

我需要保留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中完成吗?

3 个答案:

答案 0 :(得分:8)

不幸的是,你现在只能使用def get_order_details(self): items = Item.objects.filter(order=self) item_list = [item.serialize for item in items] return { 'order_details': self.serialize, 'item_list': item_list } 运算符的命名类型,你还不能使用任意的元类型值(虽然你真的应该应该能够)

假设您可以控制要比较的元类型的创建,一个实现相同结果的解决方案是创建一个包含初始化器的包装器类型,该初始化器存储执行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.selfC.selfbase

然而,这不应该是你的问题,因为我们只是从编译时已知的元类型构造D.self

但是,如果您无法控制元类型的创建(即您从API中获取它们),那么您可以使用它们做的事情受到更多限制。

作为@adev says,您可以使用type(of:)来获取给定实例的动态元类型,并使用==运算符来确定两个元类型是否相同。但是,这种方法的一个问题是它忽略了类层次结构和协议,因为子类型元类型不会与超类型元类型进行比较。

类的一个解决方案是使用Mirror,如in this Q&A所示:

AnyType

我们正在使用sequence(first:next:)从动态类型/// 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 创建一系列元类型,通过它可能具有的任何超类元类型。

但是这种方法仍无法使用协议。希望该语言的未来版本将提供更丰富的反射API,允许您比较两个元类型值之间的关系。

但是,鉴于上述有关能够使用x的知识,我们可以通过单独处理类元类型来使用它来解除上述MirrorT.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,{{1} }是P,这是协议本身的类型。目前,此类型只能包含值T.Type

答案 1 :(得分:1)

理想情况下,以下内容适用于您的情况,其中一些更改为type(of: 7)而不是7==运算符,而不是is。但是swift有一个错误,它会抛出错误。

let x = Int.self
var isInt = type(of: 7) == x //binary operator '==' cannot be applied to two 'Int.Type' operands

相反,您可以使用以下代码,它可以正常工作。

let x = Int.self
var typeOfX = type(of: 7)
var isInt = typeOfX == x //true

Apple工程师在此证实了这个错误:

Joe Groff - Twitter

问题的第一行应为var isInt = 7 is Int。请注意Int而不是Int.Type。否则Xcode会抛出警告。

enter image description here

通常在大多数情况下你可以这样做,

if z is String {
   //do something
} 

答案 2 :(得分:0)

Int.self并不总是意味着持有Int.type。

从调用MyClass.self返回的对象是MyClass的swift元类型。该对象将init函数和此类中定义的所有方法公开为curried方法(读取实例方法是Swift中的Curried函数)。

如何使用isKindOfClass?

isKindOfClass:如果接收者是指定类的实例或从指定类继承的任何类的实例,则返回YES。

参考:https://medium.com/ios-os-x-development/types-and-meta-types-in-swift-9cd59ba92295