獲取類的屬性型別和名稱,而無需例項化它

如果要為某個類的例項提取屬性的名稱型別 (Swift 3:type(of: value),Swift 2:value.dynamicType),則使用 Swift 類 Mirror 。 ****

如果你的類繼承自 NSObject,你可以使用方法 class_copyPropertyListproperty_getAttributes 來找出一個類的屬性的名稱型別 - 沒有它的例項。我為此在 Github 上建立了一個專案,但這裡是程式碼:

func getTypesOfProperties(in clazz: NSObject.Type) -> Dictionary<String, Any>? {
    var count = UInt32()
    guard let properties = class_copyPropertyList(clazz, &count) else { return nil }
    var types: Dictionary<String, Any> = [:]
    for i in 0..<Int(count) {
        guard let property: objc_property_t = properties[i], let name = getNameOf(property: property) else { continue }
        let type = getTypeOf(property: property)
        types[name] = type
    }
    free(properties)
    return types
}

func getTypeOf(property: objc_property_t) -> Any {
    guard let attributesAsNSString: NSString = NSString(utf8String: property_getAttributes(property)) else { return Any.self }
    let attributes = attributesAsNSString as String
    let slices = attributes.components(separatedBy: "\"")
    guard slices.count > 1 else { return getPrimitiveDataType(withAttributes: attributes) }
    let objectClassName = slices[1]
    let objectClass = NSClassFromString(objectClassName) as! NSObject.Type
    return objectClass
}
    
   func getPrimitiveDataType(withAttributes attributes: String) -> Any {
        guard let letter = attributes.substring(from: 1, to: 2), let type = primitiveDataTypes[letter] else { return Any.self }
        return type
    }

其中 primitiveDataTypes 是一個 Dictionary,它將屬性字串中的字母對映為值型別:

let primitiveDataTypes: Dictionary<String, Any> = [
    "c" : Int8.self,
    "s" : Int16.self,
    "i" : Int32.self,
    "q" : Int.self, //also: Int64, NSInteger, only true on 64 bit platforms
    "S" : UInt16.self,
    "I" : UInt32.self,
    "Q" : UInt.self, //also UInt64, only true on 64 bit platforms
    "B" : Bool.self,
    "d" : Double.self,
    "f" : Float.self,
    "{" : Decimal.self
]
    
   func getNameOf(property: objc_property_t) -> String? {
        guard let name: NSString = NSString(utf8String: property_getName(property)) else { return nil }
        return name as String
    }

它可以提取型別繼承自 NSObject 的所有屬性的 NSObject.Type,如 NSDate(Swift3:Date),NSString(Swift3:String?)和 NSNumber,但它儲存在 Any 型別中(如你所見)方法返回的 Dictionary 的值)。這是由於 value types 的限制,如 Int,Int32,Bool。由於這些型別不是從 NSObject 繼承的,所以在例如 Int-Int.self 上呼叫 .self 不會返回 NSObject.Type,而是返回型別 Any。因此,該方法返回 Dictionary<String, Any>? 而不是 Dictionary<String, NSObject.Type>?

你可以像這樣使用此方法:

class Book: NSObject {
    let title: String
    let author: String?
    let numberOfPages: Int
    let released: Date
    let isPocket: Bool

    init(title: String, author: String?, numberOfPages: Int, released: Date, isPocket: Bool) {
        self.title = title
        self.author = author
        self.numberOfPages = numberOfPages
        self.released = released
        self.isPocket = isPocket
    }
}

guard let types = getTypesOfProperties(in: Book.self) else { return }
for (name, type) in types {
    print("'\(name)' has type '\(type)'")
}
// Prints:
// 'title' has type 'NSString'
// 'numberOfPages' has type 'Int'
// 'author' has type 'NSString'
// 'released' has type 'NSDate'
// 'isPocket' has type 'Bool'

你也可以嘗試將 Any 轉換為 NSObject.Type,這將成功繼承 NSObject 的所有屬性,然後你可以使用標準 == 運算子檢查型別:

func checkPropertiesOfBook() {
    guard let types = getTypesOfProperties(in: Book.self) else { return }
    for (name, type) in types {
        if let objectType = type as? NSObject.Type {
            if objectType == NSDate.self {
                print("Property named '\(name)' has type 'NSDate'")
            } else if objectType == NSString.self {
                print("Property named '\(name)' has type 'NSString'")
            }
        }
    }
}

如果你宣告此自定義 == 運算子:

func ==(rhs: Any, lhs: Any) -> Bool {
    let rhsType: String = "\(rhs)"
    let lhsType: String = "\(lhs)"
    let same = rhsType == lhsType
    return same
}

然後你甚至可以像這樣檢查 value types 的型別:

func checkPropertiesOfBook() {
    guard let types = getTypesOfProperties(in: Book.self) else { return }
    for (name, type) in types {
        if type == Int.self {
            print("Property named '\(name)' has type 'Int'")
        } else if type == Bool.self {
            print("Property named '\(name)' has type 'Bool'")
        }
    }
}

限制value types 是可選項時,此解決方案不起作用。如果你在 NSObject 子類中宣告瞭一個屬性,如下所示:var myOptionalInt: Int?,上面的程式碼將找不到該屬性,因為方法 class_copyPropertyList 不包含可選的值型別。