受 @arndt-bieberstein 的 ObjC 回答启发,我用 Swift 3 写了一个解决方案(在 Swift 的早期版本中可能非常相似,如果不是相同的话)。你可以在 Github 上找到它:
Github。我正在尝试制作一个 pod,但使用 Swift 3 代码时遇到了问题(可能与 CLI
xcodebuild
或 Xcode 8 相关)。无论如何,类方法
func getTypesOfProperties(inClass clazz: NSObject.Type) -> Dictionary<String, Any>?
可以提取任何继承自
NSObject
的 Swift 类的名称和类型。该项目的核心是这些方法,但请在 Github 上查看完整代码:
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
}
func getNameOf(property: objc_property_t) -> String? {
guard let name: NSString = NSString(utf8String: property_getName(property)) else { return nil }
return name as String
}
这个方法可以提取所有属性的
NSObject.Type
,这些属性的类类型都继承自
NSObject
,例如
NSDate
(Swift3:
Date
)、
NSString
(Swift3:
String
?)和
NSNumber
。然而,它存储在类型
Any
中(正如该方法返回的Dictionary值的类型所示)。这是由于一些
值类型
的限制,例如Int、Int32、Bool。因为这些类型不继承自NSObject,所以在Int上调用
.self
(例如
Int.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(inClass: Book.self) else { return }
for (name, type) in types {
print("'\(name)' has type '\(type)'")
}
您也可以尝试将Any
转换为NSObject.Type
,这适用于继承自NSObject
的所有属性,然后可以使用标准的==
运算符检查类型:
func checkPropertiesOfBook() {
guard let types = getTypesOfProperties(inClass: 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
}
你甚至可以像这样检查值类型
的类型:
func checkPropertiesOfBook() {
guard let types = getTypesOfProperties(inClass: 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
无法找到这些属性。
有人有解决办法吗?