Поиск…


Синтаксис

  • Зеркало (отражающее: экземпляр) // Инициализирует зеркало с объектом для отражения
  • mirror.displayStyle // Стиль отображения, используемый для игровых площадок Xcode
  • mirror.description // Текстовое представление этого экземпляра, см. CustomStringConvertible
  • mirror.subjectType // Возвращает тип отраженного объекта
  • mirror.superclassMirror // Возвращает зеркало суперкласса объекта, который отражается

замечания

  1. Основные пометки:

Mirror - это struct используемая при интроспекции объекта в Свифте. Его наиболее заметным свойством является массив детей. Одним из возможных вариантов использования является сериализация структуры для Core Data . Это делается путем преобразования struct в объект NSManagedObject .

  1. Основное использование для зеркальных заметок:

children свойство Mirror представляет собой массив дочерних объектов из объекта экземпляр Зеркала отражает. У child объекта есть две label и value свойств. Например, ребенок может быть свойством с названием title и значением Game of Thrones: A Song of Ice and Fire .

Основное использование для зеркала

Создание класса для объекта Mirror

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

Создание экземпляра, который на самом деле будет предметом зеркала. Также здесь вы можете добавить значения к свойствам класса Project.

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

В приведенном ниже коде показано создание экземпляра Mirror. Свойство AnyForwardCollection<Child> зеркала - AnyForwardCollection<Child> где Child является кортежем typealias для свойства и значения объекта. Child была label: String и 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)")
}

Вывод на игровой площадке или консоли в Xcode для цикла for выше.

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

Протестировано на игровой площадке на Xcode 8 beta 2

Получение типа и имен свойств для класса без необходимости его экземпляра

Использование класса Swift Mirror работает, если вы хотите извлечь имя , значение и тип (Swift 3: type(of: value) , Swift 2: value.dynamicType ) свойств для экземпляра определенного класса.

Если вы наследуете класс из NSObject , вы можете использовать метод class_copyPropertyList вместе с property_getAttributes чтобы узнать имя и типы свойств для класса - без его экземпляра . Для этого я создал проект для Github , но вот код:

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 - это словарь, сопоставляющий букву в строке атрибута с типом значения:

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
    }

Он может извлечь NSObject.Type всех свойств, которые наследует тип класса из NSObject таких как NSDate (Swift3: Date ), NSString (Swift3: String ?) И NSNumber , однако он является хранилищем в типе Any (как вы можете видеть как тип значения словаря, возвращаемого методом). Это связано с ограничениями value types такими как Int, Int32, Bool. Поскольку эти типы не наследуют от NSObject, вызывая .self на например, Int - Int.self не возвращает NSObject.Type, а тип Any . Таким образом, метод возвращает Dictionary<String, Any>? а не Dictionary<String, NSObject.Type>? ,

Вы можете использовать этот метод следующим образом:

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'

Вы также можете попробовать забрасывать Any в NSObject.Type , который сменит для всех свойств , наследующих от NSObject , то вы можете проверить тип , используя стандарт == оператор:

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

Если вы объявите этот пользовательский == operator:

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

Затем вы можете проверить тип value types следующим образом:

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

ОГРАНИЧЕНИЯ Это решение не работает, если value types являются опциями. Если вы объявили свойство в вашем подклассе var myOptionalInt: Int? следующим образом: var myOptionalInt: Int? , код выше не найдет это свойство, потому что метод class_copyPropertyList не содержит необязательные типы значений.



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow