Recherche…


Syntaxe

  • Miroir (reflétant: instance) // Initialise un miroir avec l'objet à réfléchir
  • mirror.displayStyle // Style d'affichage utilisé pour les terrains de jeux Xcode
  • mirror.description // Représentation textuelle de cette instance, voir CustomStringConvertible
  • mirror.subjectType // Retourne le type du sujet réfléchi
  • mirror.superclassMirror // Retourne le miroir de la super-classe du sujet réfléchi

Remarques

  1. Remarques générales:

Un Mirror est une struct utilisée dans l'introspection d'un objet dans Swift. Sa propriété la plus importante est le tableau des enfants. Un cas d'utilisation possible est de sérialiser une structure pour les Core Data . Cela se fait en convertissant une struct en un NSManagedObject .

  1. Utilisation de base pour Mirror Remarques:

La propriété children d'un Mirror est un tableau d'objets enfants provenant de l'objet que l'instance miroir reflète. Un objet child a deux propriétés label et value . Par exemple, un enfant peut être une propriété avec le title et la valeur de Game of Thrones: A Song of Ice and Fire .

Utilisation de base pour miroir

Créer la classe pour être le sujet du miroir

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

Créer une instance qui sera effectivement le sujet du miroir. Ici aussi, vous pouvez ajouter des valeurs aux propriétés de la classe Project.

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

Le code ci-dessous montre la création de l'instance Mirror. La propriété children du miroir est AnyForwardCollection<Child>Child est typealias tuple pour la propriété et la valeur de l'objet. Child avait une label: String et 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)")
}

Sortie dans Playground ou Console dans Xcode pour la boucle for ci-dessus.

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

Testé sur Playground sur Xcode 8 beta 2

Obtenir le type et les noms des propriétés d'une classe sans avoir à l'instancier

L'utilisation de la classe Swift Mirror fonctionne si vous souhaitez extraire le nom , la valeur et le type (Swift 3: type(of: value) , Swift 2: value.dynamicType ) des propriétés d'une instance d'une certaine classe.

Si votre classe hérite de NSObject , vous pouvez utiliser la méthode class_copyPropertyList avec property_getAttributes pour rechercher le nom et les types de propriétés d'une classe, sans en avoir une instance . J'ai créé un projet sur Github pour cela, mais voici le 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
    }

primitiveDataTypes est un dictionnaire mappant une lettre dans la chaîne d'attribut à un type de valeur:

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
    }

Il peut extraire le NSObject.Type de toutes les propriétés dont le type de classe hérite de NSObject telles que NSDate (Swift3: Date ), NSString (Swift3: String ?) Et NSNumber , mais il est NSNumber dans le type Any (comme vous pouvez le voir type de la valeur du dictionnaire renvoyé par la méthode). Cela est dû aux limitations des value types de value types tels que Int, Int32, Bool. Comme ces types n'héritent pas de NSObject, appeler .self , par exemple, un objet Int- Int.self ne retourne pas NSObject.Type, mais plutôt le type Any . Ainsi, la méthode renvoie Dictionary<String, Any>? et pas Dictionary<String, NSObject.Type>? .

Vous pouvez utiliser cette méthode comme ceci:

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'

Vous pouvez également essayer de convertir le Any en NSObject.Type , qui réussira pour toutes les propriétés héritant de NSObject , vous pourrez alors vérifier le type en utilisant l'opérateur standard == :

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 vous déclarez cet opérateur personnalisé == :

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

Vous pouvez même vérifier le type de type de value types comme ceci:

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

LIMITES Cette solution ne fonctionne pas lorsque les value types sont optionnels. Si vous avez déclaré une propriété dans votre sous-classe NSObject comme ceci: var myOptionalInt: Int? , le code ci-dessus ne trouvera pas cette propriété car la méthode class_copyPropertyList ne contient pas de types de valeur facultatifs.



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow