獲取類的屬性型別和名稱,而無需例項化它
如果要為某個類的例項提取屬性的名稱,值和型別 (Swift 3:type(of: value)
,Swift 2:value.dynamicType
),則使用 Swift 類 Mirror
。 ****
如果你的類繼承自 NSObject
,你可以使用方法 class_copyPropertyList
和 property_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
不包含可選的值型別。