2017-07-02 150 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

相關問題