Sök…


Syntax

  • Spegel (reflekterande: instans) // Initierar en spegel med motivet att reflektera
  • mirror.displayStyle // Displaystil som används för Xcode-lekplatser
  • mirror.description // Textrepresentation av den här instansen, se CustomStringConvertible
  • mirror.subjectType // Returnerar typen av motiv som reflekteras
  • mirror.superclassMirror // Returnerar spegeln för superklassen för motivet som reflekteras

Anmärkningar

  1. Allmänna kommentarer:

En Mirror är en struct används i introspektionen av ett objekt i Swift. Den mest framstående egenskapen är barngruppen. Ett möjligt användningsfall är att serialisera en struktur för Core Data . Detta görs genom att konvertera en struct till ett NSManagedObject .

  1. Grundläggande användning för spegelanmärkningar:

Den children egenskap hos en Mirror är en array med underordnade objekt från objektet Mirror instans reflekterar. Ett child objekt har två egenskaper label och value . Till exempel kan ett barn vara en egenskap med namnet title och värdet av Game of Thrones: A Song of Ice and Fire .

Grundläggande användning för spegel

Att skapa klassen för att vara ämnet för spegeln

class Project {
    var title: String = ""
    var id: Int = 0
    var platform: String = ""
    var version: Int = 0
    var info: String?
}

Skapa en instans som faktiskt kommer att bli föremål för spegeln. Här kan du också lägga till värden i egenskaperna för projektklassen.

let sampleProject = Project()
sampleProject.title = "MirrorMirror"
sampleProject.id = 199
sampleProject.platform = "iOS"
sampleProject.version = 2
sampleProject.info = "test app for Reflection"

Koden nedan visar skapandet av Mirror-instansen. Spegelns egendom för barn är en AnyForwardCollection<Child> där Child är typealias-tupel för motivets egendom och värde. Child hade en label: String och 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)")
}

Output i Playground eller Console i Xcode för for-loopen ovan.

title:MirrorMirror
id:199
platform:iOS
version:2
info:Optional("test app for Reflection")

Testat i Playground på Xcode 8 beta 2

Få typ och namn på egenskaper för en klass utan att behöva instansera det

Användning av Swift-klassen Mirror fungerar om du vill extrahera namn , värde och typ (Swift 3: type(of: value) , Swift 2: value.dynamicType ) för egenskaper för en instans av en viss klass.

Om du klassar arv från NSObject kan du använda metoden class_copyPropertyList tillsammans med property_getAttributes att ta reda på namnet och typerna av egenskaper för en klass - utan att ha en instans av den . Jag skapade ett projekt på Github för detta, men här är koden:

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
    }

Där primitiveDataTypes är en ordbok som mappar en bokstav i attributsträngen till en värdetyp:

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
    }

Det kan extrahera NSObject.Type av alla egenskaper som NSObject ärver från NSObject som NSDate (Swift3: Date ), NSString (Swift3: String ?) Och NSNumber , men det är lagrat i typen Any (som du kan se som typ av värdet på ordlistan som returneras med metoden). Detta beror på begränsningarna av value types som Int, Int32, Bool. Eftersom dessa typer inte ärver från NSObject .self på t.ex. en Int - Int.self NSObject.Type utan snarare typen Any . Således returnerar metoden Dictionary<String, Any>? och inte Dictionary<String, NSObject.Type>? .

Du kan använda den här metoden så här:

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'

Du kan också försöka kasta Any till NSObject.Type , som lyckas för alla egenskaper som ärver från NSObject , då kan du kontrollera typen med standard == operator:

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'")
            }
        }
    }
}

Om du förklarar den här anpassade operatören == :

func ==(rhs: Any, lhs: Any) -> Bool {
    let rhsType: String = "\(rhs)"
    let lhsType: String = "\(lhs)"
    let same = rhsType == lhsType
    return same
}

Du kan till och med kontrollera vilken typ av value types som denna:

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'")
        }
    }
}

BEGRÄNSNINGAR Denna lösning fungerar inte när value types är tillval. Om du har deklarerat en egenskap i din NSObject-underklass så här: var myOptionalInt: Int? , koden ovan hittar inte den egenskapen eftersom metoden class_copyPropertyList inte innehåller valfria värdetyper.



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow