Buscar..


Sintaxis

  • Espejo (reflejando: instancia) // Inicializa un espejo con el sujeto a reflejar
  • mirror.displayStyle // Estilo de visualización utilizado para los parques infantiles Xcode
  • mirror.description // Representación textual de esta instancia, vea CustomStringConvertible
  • mirror.subjectType // Devuelve el tipo del tema que se refleja
  • mirror.superclassMirror // Devuelve el espejo de la superclase del sujeto que se refleja

Observaciones

  1. Observaciones generales:

Un Mirror es una struct utilizada en la introspección de un objeto en Swift. Su propiedad más destacada es la matriz infantil. Un posible caso de uso es serializar una estructura para Core Data . Esto se hace al convertir una struct en un objeto NSManagedObject .

  1. Uso básico para comentarios de espejo:

El children propiedad de un Mirror es una matriz de objetos secundarios del objeto de la instancia espejo está reflejando. Un objeto child tiene dos propiedades label y value . Por ejemplo, un niño puede ser una propiedad con el nombre title y el valor de Game of Thrones: A Song of Ice and Fire .

Uso básico para espejo

Creando la clase para ser el sujeto del espejo.

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

Creando una instancia que realmente será el sujeto del espejo. También aquí puede agregar valores a las propiedades de la clase Proyecto.

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

El siguiente código muestra la creación de la instancia de Mirror. La propiedad children del espejo es una AnyForwardCollection<Child> donde Child es typealias tuple para la propiedad y el valor del sujeto. Child tenía una label: String y 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)")
}

Salida en Playground o Console en Xcode para el bucle for anterior.

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

Probado en Playground en Xcode 8 beta 2

Obtención de tipos y nombres de propiedades para una clase sin tener que instanciarla

El uso de la clase Swift Mirror funciona si desea extraer el nombre , el valor y el tipo (Swift 3: type(of: value) , Swift 2: value.dynamicType ) de las propiedades para una instancia de una determinada clase.

Si la clase hereda de NSObject , puede usar el método class_copyPropertyList junto con property_getAttributes para averiguar el nombre y los tipos de propiedades de una clase, sin tener una instancia de ello . Creé un proyecto en Github para esto, pero aquí está el código:

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
    }

Donde primitiveDataTypes es un Diccionario que asigna una letra en la cadena de atributo a un tipo de valor:

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
    }

Puede extraer el NSObject.Type de todas las propiedades que tipo de clase hereda de NSObject como NSDate (Swift3: Date ), NSString (Swift3: String ?) Y NSNumber , sin embargo, se almacena en el tipo Any (como se puede ver como Tipo del valor del diccionario devuelto por el método). Esto se debe a las limitaciones de los value types de value types , como Int, Int32, Bool. Desde esos tipos no heredan de NSObject, llamando .self sobre, por ejemplo un Int - Int.self no vuelve NSObject.Type, sino más bien del tipo Any . Por lo tanto, el método devuelve Dictionary<String, Any>? y no Dictionary<String, NSObject.Type>? .

Puedes usar este método así:

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'

También puede intentar convertir el Any en NSObject.Type , que tendrá éxito para todas las propiedades NSObject de NSObject , luego puede verificar el tipo utilizando el operador estándar == :

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

Si declara este operador personalizado == :

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

Incluso puedes verificar el tipo de value types de value types como este:

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

LIMITACIONES Esta solución no funciona cuando los value types son opcionales. Si ha declarado una propiedad en su subclase de NSObject como esta: var myOptionalInt: Int? , el código anterior no encontrará esa propiedad porque el método class_copyPropertyList no contiene tipos de valor opcionales.



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow