2017-07-02 132 views
4

我需要保留Swift元类型的集合,并编写一个函数来检查给定对象是否是其中一个元素的实例。我能做到这一点很容易在Java中:检查Swift对象是否为给定元类型的实例

Class c = x.getClass(); 
c.isInstance(someObj) 

但是,我不知道该怎么做的雨燕:

var isInt = 7 is Int.Type // compiles 

let x = Int.self 
var isInt = 7 is x // compiler error - Use of undeclared type 'x' 

这甚至可能在斯威夫特做?

+2

'var isInt = 7 is Int.Type' does not actually does not work。你的意思是'var isInt = 7 is Int' – vadian

+0

它的工作原理是检查“type”的语法,它包含“type - > metatype-type” - https://developer.apple.com/library/content/documentation /雨燕/概念/ Swift_Programming_Language /类型。html#// apple_ref/swift/grammar/type – frangulyan

+0

哦,对不起,通过“作品”我的意思是“编译”:) – frangulyan

回答

4

不幸的是,你目前只能使用一个命名的类型与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将为。

但是这不应该是一个问题,因为我们只是从编译时已知的元类型构造AnyType


然而,如果你不拥有控制权创建元类型的(即你得到从API递了),那么你相当多的限制与您可以与他们做什么。

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

一种解决方案是使用Mirror,同样如图in this Q&A:我们使用sequence(first:next:)通过任何超类来创建从动态类型的x元类型的序列元类型它

/// 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,以便您可以比较两个元类型值之间的关系。


然而,考虑到能够通过单独处理类元类型使用Mirror,我们可以用它来从我们AnyType包装解除T.self == base上述限制上述知识:

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是类metatype应该是唯一的情况,其中T.self != base,与协议一样,当T是某些协议P,T.TypeP.Protocol,这是pro的类型tocol本身。而目前,这种类型只能保存价值P.self

+0

有一个通用的初始化程序并在关闭中“捕获”T的奇妙想法!非常感谢。 –

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

+0

你是什么意思“Int.self并不总是意味着持有Int.type”? 'Int.self'是一个元类型值,类型为'Int.Type'。而且因为'Int'是一个值类型,所以'Int.Type'元类型唯一的值就是'Int.self'。 – Hamish

+0

如何在swift中使用“isKindOfClass”? – frangulyan

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 

苹果公司的工程师已经在这里证实了这个错误:

Joe Groff - Twitter

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

enter image description here

通常在你可能只是做大多数情况下,

if z is String { 
    //do something 
} 
+0

只是一个小小的评论 - Int.Type也适用,我已经回答了以上评论的链接:) – frangulyan

+0

@frangulyan检查现在添加的图像。它不会像显示的警告中所示的那样工作。这总会失败。 – adev

+0

是的,你是对的,我的意思是“编译”,纠正自己:)我的问题是让它首先编译,这就是为什么我现在没有考虑结果:) – frangulyan

相关问题