Ispirato dalla risposta objC da @ Arndt-Bieberstein Ho scritto una soluzione in Swift 3 (probabilmente molto simile - se non stessi - nelle versioni precedenti di Swift). Lo trovo su Github Sto cercando di farne un pod ma sto avendo problemi con pob lib lint
per lavorare con il codice Swift 3 (probabilmente il problema relativo a CLI xcodebuild
o Xcode 8). In ogni caso, il metodo di classe func getTypesOfProperties(inClass clazz: NSObject.Type) -> Dictionary<String, Any>?
può estrarre il nome e tipi di qualsiasi classe Swift che eredita da NSObject
.
Il cavallo lavoro del progetto sono questi metodi, ma checkout il codice completo su 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
}
E 'possibile estrarre il NSObject.Type
di tutte le proprietà che tipo di classe eredita dalla NSObject
come NSDate
(Swift3: Date
), NSString
(Swift3: String
?) E NSNumber
, tuttavia è memorizzato nel tipo Any
(come è possibile vedere come il tipo di valore del dizionario restituito dal metodo). Ciò è dovuto alle limitazioni di value types
come Int, Int32, Bool. Poiché questi tipi non ereditano da NSObject, chiamando .self
su es. un Int - Int.self
non restituisce NSObject.Type, ma il tipo Any
. Pertanto, il metodo restituisce Dictionary<String, Any>?
e non Dictionary<String, NSObject.Type>?
.
È possibile utilizzare questo metodo come questo:
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)'")
}
// Prints:
// 'title' has type 'NSString'
// 'numberOfPages' has type 'Int'
// 'author' has type 'NSString'
// 'released' has type 'NSDate'
// 'isPocket' has type 'Bool'
Potete anche provare a lanciare il Any
-NSObject.Type
, che avrà successo per tutte le proprietà che ereditano da NSObject
, allora si può verificare il tipo utilizzando ==
operatore di serie :
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'")
}
}
}
}
Se si dichiara questa usanza ==
operatore:
func ==(rhs: Any, lhs: Any) -> Bool {
let rhsType: String = "\(rhs)"
let lhsType: String = "\(lhs)"
let same = rhsType == lhsType
return same
}
È quindi possibile anche verificare il tipo di value types
come questo:
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'")
}
}
}
LIMITAZIONI perché non sono ancora stati in grado di dare a questo progetto di sostegno per quando il value types
sono optional. Se hai dichiarato una proprietà nella tua sottoclasse NSObject come questa: var myOptionalInt: Int?
la mia soluzione non funzionerà, perché il metodo class_copyPropertyList
non riesce a trovare quelle proprietà.
Qualcuno ha una soluzione per questo?
La dichiarazione if non è ridondante? Se typeAttribute ha quel prefisso, non ha automaticamente una lunghezza> 1 dato che il prefisso è di 2 caratteri? – rvijay007
Hai ragione. Lo aggiusterò! –
Questa è una cosa molto carina da sapere, e sono contento di aver trovato la tua risposta! Questo è assolutamente ottimo per analizzare bullsh * t nelle classi ObjC, ad esempio quando i ragazzi dell'API hanno deciso di inviare tutto come stringhe :(:(:( –