Zoeken…


Syntaxis

  • Spiegel (reflecterend: instantie) // Initialiseert een spiegel met het te reflecteren onderwerp
  • mirror.displayStyle // Weergavestijl gebruikt voor Xcode-speeltuinen
  • mirror.description // Tekstuele weergave van deze instantie, zie CustomStringConvertible
  • mirror.subjectType // Retourneert het type van het onderwerp dat wordt weerspiegeld
  • mirror.superclassMirror // Retourneert de spiegel van de superklasse van het onderwerp dat wordt weerspiegeld

Opmerkingen

  1. Algemene opmerkingen:

Een Mirror is een struct gebruikt bij de introspectie van een object in Swift. Het meest prominente bezit is de reeks kinderen. Een mogelijke use case is om een struct voor Core Data te serialiseren. Dit wordt gedaan door een struct converteren naar een NSManagedObject .

  1. Basisgebruik voor spiegelopmerkingen:

De children eigenschap van een Mirror is een array van onderliggende objecten van het object de spiegel instantie reflecteert. Een child object heeft twee eigenschappen label en value . Een kind kan bijvoorbeeld een eigenschap zijn met de title en de waarde van Game of Thrones: A Song of Ice and Fire .

Basisgebruik voor spiegel

De klasse maken als onderwerp van de spiegel

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

Een instantie maken die daadwerkelijk het onderwerp van de spiegel zal zijn. Ook hier kunt u waarden toevoegen aan de eigenschappen van de klasse Project.

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

De onderstaande code toont het maken van de instantie Mirror. De eigenschap AnyForwardCollection<Child> van de spiegel is een AnyForwardCollection<Child> waarbij Child typalias tuple is voor de eigenschap en waarde van het onderwerp. Child had een label: String en 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)")
}

Uitvoer in Speeltuin of Console in Xcode voor de for-lus hierboven.

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

Getest in Playground op Xcode 8 beta 2

Type en namen van eigenschappen voor een klasse ophalen zonder deze te instantiëren

Het gebruik van de Swift-klasse Mirror werkt als u naam , waarde en type (Swift 3: type(of: value) , Swift 2: value.dynamicType ) van eigenschappen voor een instantie van een bepaalde klasse wilt extraheren.

Als uw klasse NSObject van NSObject , kunt u de methode class_copyPropertyList samen met property_getAttributes gebruiken om de naam en typen eigenschappen voor een klasse te achterhalen - zonder er een instantie van te hebben . Ik heb hiervoor een project op Github gemaakt , maar hier is de 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
    }

Waar primitiveDataTypes een woordenboek is dat een letter in de kenmerkreeks aan een waardetype toewijst:

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
    }

Het kan het NSObject.Type van alle eigenschappen die het klasse-type van NSObject erft, zoals NSDate (Swift3: Date ), NSString (Swift3: String ?) En NSNumber , maar het wordt opgeslagen in het type Any (zoals u kunt zien als de type van de waarde van het woordenboek geretourneerd door de methode). Dit komt door de beperkingen van value types zoals Int, Int32, Bool. Omdat deze typen niet erven van NSObject, .self op bijvoorbeeld een Int - Int.self niet NSObject.Type, maar eerder het type Any . Dus de methode retourneert Dictionary<String, Any>? en niet Dictionary<String, NSObject.Type>? .

U kunt deze methode als volgt gebruiken:

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'

Je kunt ook proberen de Any naar NSObject.Type te NSObject.Type , wat zal lukken voor alle eigenschappen die van NSObject , dan kun je het type controleren met de standaard == 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'")
            }
        }
    }
}

Als u deze aangepaste operator == declareert:

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

U kunt dan zelfs het type value types als volgt controleren:

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

BEPERKINGEN Deze oplossing werkt niet als value types optioneel zijn. Als u een eigenschap in uw NSObject-subklasse als volgt hebt aangegeven: var myOptionalInt: Int? , zal de bovenstaande code die eigenschap niet vinden omdat de methode class_copyPropertyList geen optionele waardetypen bevat.



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow