Swift Language
Reflexion
Suche…
Syntax
- Spiegeln (reflektierend: Instanz) // Initialisiert einen Spiegel mit dem zu reflektierenden Objekt
- mirror.displayStyle // Anzeigeart für Xcode-Spielplätze
- mirror.description // Textuelle Darstellung dieser Instanz, siehe CustomStringConvertible
- mirror.subjectType // Gibt den Typ des Betreffs zurück, der reflektiert wird
- mirror.superclassMirror // Gibt die Spiegelung der Superklasse des Objekts zurück, das reflektiert wird
Bemerkungen
- Allgemeine Bemerkungen:
Ein Mirror
ist eine struct
die zur Introspektion eines Objekts in Swift verwendet wird. Die prominenteste Eigenschaft ist das Kinderarray. Ein möglicher Anwendungsfall ist das Serialisieren einer Struktur für Core Data
. Dies geschieht durch Konvertieren einer struct
in ein NSManagedObject
.
- Grundlegende Verwendung für Spiegeln Anmerkungen:
Die children
Eigenschaft eines Mirror
ist ein Array von untergeordneten Objekten des Objekts, das von der Spiegelungsinstanz reflektiert wird. Ein child
- Objekt hat zwei Eigenschaften label
und value
. Zum Beispiel kann ein Kind könnte eine Eigenschaft mit dem Namen seinen title
und dem Wert von Game of Thrones: A Song of Ice and Fire
.
Grundlegende Verwendung für Spiegel
Erstellen der Klasse als Subjekt des Spiegels
class Project {
var title: String = ""
var id: Int = 0
var platform: String = ""
var version: Int = 0
var info: String?
}
Erstellen einer Instanz, die tatsächlich Gegenstand des Spiegels ist. Auch hier können Sie den Eigenschaften der Project-Klasse Werte hinzufügen.
let sampleProject = Project()
sampleProject.title = "MirrorMirror"
sampleProject.id = 199
sampleProject.platform = "iOS"
sampleProject.version = 2
sampleProject.info = "test app for Reflection"
Der folgende Code zeigt die Erstellung der Mirror-Instanz. Die Child
-Eigenschaft des Spiegels ist AnyForwardCollection<Child>
wobei Child
ein Typealias-Tupel für die Eigenschaft und den Wert des Subjekts ist. Child
hatte ein label: String
und value: Any
.
let projectMirror = Mirror(reflecting: sampleProject)
let properties = projectMirror.children
print(properties.count) //5
print(properties.first?.label) //Optional("title")
print(properties.first!.value) //MirrorMirror
print()
for property in properties {
print("\(property.label!):\(property.value)")
}
Ausgabe in Playground oder Console in Xcode für die obige for-Schleife.
title:MirrorMirror
id:199
platform:iOS
version:2
info:Optional("test app for Reflection")
Getestet in Playground auf Xcode 8 Beta 2
Typ und Namen von Eigenschaften für eine Klasse abrufen, ohne sie instanziieren zu müssen
Die Verwendung der Swift-Klasse Mirror
funktioniert, wenn Sie Name , Wert und Typ (Swift 3: type(of: value)
, Swift 2: value.dynamicType
) von Eigenschaften für eine Instanz einer bestimmten Klasse extrahieren möchten.
Wenn Sie eine Klasse von NSObject
, können Sie die Methode class_copyPropertyList
zusammen mit property_getAttributes
, um den Namen und die Typen von Eigenschaften für eine Klasse herauszufinden, ohne eine Instanz davon zu haben . Ich habe dafür ein Projekt auf Github erstellt , aber hier ist der Code:
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
}
Wobei primitiveDataTypes
ein Dictionary ist, das einen Buchstaben in der Attributzeichenfolge einem Werttyp zuordnet:
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
}
Es kann den NSObject.Type
aus allen Eigenschaften extrahieren, deren NSObject.Type
von NSObject.Type
erbt, NSObject
z. B. NSDate
(Swift3: Date
), NSString
(Swift3: String
?) Und NSNumber
Der Typ ist jedoch im Typ Any
gespeichert (wie Sie als sehen können Typ des von der Methode zurückgegebenen Wertes des Dictionary). Dies ist aufgrund der Einschränkungen von value types
wie Int, Int32, Bool. Da diese Typen nicht von NSObject geerbt werden, gibt der Aufruf von sich .self
für zB Int - Int.self
nicht NSObject.Type zurück, sondern den Typ Any
. Die Methode gibt also Dictionary<String, Any>?
und nicht Dictionary<String, NSObject.Type>?
.
Sie können diese Methode folgendermaßen verwenden:
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'
Sie können auch versuchen, Any
in NSObject.Type
ist für alle von NSObject
Eigenschaften NSObject
. Anschließend können Sie den Typ mit dem Standardoperator ==
prüfen:
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'")
}
}
}
}
Wenn Sie diesen benutzerdefinierten Operator ==
angeben:
func ==(rhs: Any, lhs: Any) -> Bool {
let rhsType: String = "\(rhs)"
let lhsType: String = "\(lhs)"
let same = rhsType == lhsType
return same
}
Sie können sogar die Art der value types
wie folgt überprüfen:
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'")
}
}
}
Diese Lösung GRENZEN funktioniert nicht , wenn value types
sind optionals. Wenn Sie eine Eigenschaft in Ihrer NSObject-Unterklasse wie folgt deklariert haben: var myOptionalInt: Int?
Der obige Code kann diese Eigenschaft nicht finden, da die Methode class_copyPropertyList
keine optionalen class_copyPropertyList
enthält.