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

  1. 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 .

  1. 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.



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow