Чи підтримує Свіфт відображення?


113

Чи підтримує Свіфт відображення? наприклад, є щось на зразок valueForKeyPath:і setValue:forKeyPath:для об'єктів Swift?

Насправді у нього навіть є система динамічного типу, щось на зразок obj.classоб’єктива-C?


1
Я створив клас помічників для роздумів у Свіфті. Ви можете знайти його за адресою: github.com/evermeer/EVReflection
Едвін Вермер

2
Вони видалили відображення в межах Swift 2.0. Ось так я перераховую атрибути та значення Посилання
mohacs

Відповіді:


85

Схоже, початок деякої підтримки роздумів:

class Fruit {
    var name="Apple"
}

reflect(Fruit()).count         // 1
reflect(Fruit())[0].0          // "name"
reflect(Fruit())[0].1.summary  // "Apple"

З mchambers gist тут: https://gist.github.com/mchambers/fb9da554898dae3e54f2


5
Ну, я б не вважав це справжнім відображенням. По-перше, це лише заново. Мені здається, це просто хак, щоб включити налагодження в Xcode. Протокол Mirrorфактично цитує це слово IDEкілька разів.
Султан

7
І працює лише на властивості. Відсутній метод відображення.
Султан

11
Автор перевірки суті. Я написав це в лабораторії Свіфта в WWDC, зрозумів, що я поділюся рештою. Як усі зрозуміли, інженери, з якими я розмовляв, підтвердили функцію refle () для підтримки ігрового майданчика. Але ви все ще можете повеселитися з ним :) ось я зламав невеликий модельний серіалізатор, використовуючи його. Вставте його на майданчик і веселіться
Marc Chambers

1
Подивіться відповідь stackoverflow.com/a/25345461/292145, щоб дізнатися, як _stdlib_getTypeNameможна допомогти.
Клаас

1
Ось клас, який буде відображати базові класи та необов'язкові (не типи) та має підтримку NSC кодування та розбору з та до словника: github.com/evermeer/EVCloudKitDao/blob/master/AppMessage/…
Едвін Вермер

44

Якщо клас поширюється NSObject, то вся самоаналіз і динамізм Objective-C працює. Це включає:

  • Можливість запитувати клас про його методи та властивості та викликати методи чи встановлювати властивості.
  • Можливість обміну реалізаціями методів. (додайте функціональність до всіх примірників).
  • Можливість генерування та призначення нового підкласу на льоту. (додати функціональність до заданого екземпляра)

Одним з недоліків цієї функції є підтримка додаткових типів значень Swift. Наприклад, Int властивості можна перерахувати та змінити, але Int? властивості не можуть. Необов’язкові типи можна перерахувати частково за допомогою відображення / MirrorType, але все ще не змінювати.

Якщо клас не поширюється NSObject, то працює лише нове, дуже обмежене (і триває?) Відображення (див. Відображення / MirrorType), що додає обмежену можливість запитувати екземпляр про його клас та властивості, але жодна з додаткових функцій, наведених вище .

Якщо ви не поширюєте NSObject або не використовуєте директиву "@objc", Swift за замовчуванням відправляє статичну та Vtable відправлення. Це швидше, однак за відсутності віртуальної машини не допускається перехоплення методом виконання. Цей перехоплення є основоположною частиною какао і вимагається для таких типів ознак:

  • Какао елегантні спостерігачі власності. (Спостерігачі майна випікаються прямо на мові Swift).
  • Неінвазивно застосовують наскрізні проблеми, такі як ведення журналів, управління транзакціями (тобто програмування, орієнтоване на аспекти).
  • Проксі, переадресація повідомлень тощо.

Тому рекомендується, щоб пункти в програмах Cocoa / CocoaTouch реалізовані разом із Swift:

  • Розширити від NSObject. Новий діалог класу в Xcode спрямовує в цьому напрямку.
  • Якщо накладні витрати динамічної диспетчеризації призводять до проблем продуктивності, то статичну диспетчеризацію можна використовувати - у вузьких петлях із викликами методів із дуже маленькими тілами, наприклад.

Підсумок:

  • Швидкий може поводитись як C ++, з швидкою статичною / стабільною розсилкою та обмеженим відображенням. Це робить його придатним для застосувань нижчого рівня або продуктивності, але без складності, кривої навчання або ризику помилок, пов'язаних із C ++
  • Хоча Swift є мовою компіляції, стиль повідомлень виклику методів додає самоаналізу та динамічності, які можна знайти у сучасних мовах, таких як Ruby та Python, подібно до Objective-C, але без застарілого синтаксису Objective-C.

Довідкові дані: Накладні витрати для викликів методу:

  • статичний: <1,1ns
  • vtable: ~ 1,1 сек
  • динамічний: ~ 4,9 с

(фактична продуктивність залежить від обладнання, але співвідношення залишаться схожими).

Також динамічний атрибут дозволяє нам чітко доручити Swift, що метод повинен використовувати динамічну диспетчеризацію, і тому підтримуватиме перехоплення.

public dynamic func foobar() -> AnyObject {
}

2
Навіть використовуючи методи Objective-C, схоже, це не працює для необов'язкових типів Swift. Я б запропонував зазначити це обмеження у відповіді, якщо я не пропущу хитрість.
Уїтніленд

8

Документація говорить про систему динамічного типу, в основному про

Type і dynamicType

Див. Тип метатипу (у мовній довідці)

Приклад:

var clazz = TestObject.self
var instance: TestObject = clazz()

var type = instance.dynamicType

println("Type: \(type)") //Unfortunately this prints only "Type: Metatype"

Тепер припущення TestObjectпоширюєтьсяNSObject

var clazz: NSObject.Type = TestObject.self
var instance : NSObject = clazz()

if let testObject = instance as? TestObject {
    println("yes!") //prints "yes!"
}

Наразі відображення не реалізовано.

EDIT: Я, мабуть, помилявся, дивіться відповідь stevex. Існує просте відображення лише для читання властивостей, можливо, щоб IDE могли перевіряти вміст об'єкта.


6

Схоже, що інтерфейс відбиття Swift наразі не є пріоритетним для Apple. Але крім відповіді @stevex, у стандартній бібліотеці є ще одна функція, яка допомагає.

З бета-версії 6 _stdlib_getTypeNameотримує назву змінної типу змінної. Вставте це в порожній майданчик:

import Foundation

class PureSwiftClass {
}

var myvar0 = NSString() // Objective-C class
var myvar1 = PureSwiftClass()
var myvar2 = 42
var myvar3 = "Hans"

println( "TypeName0 = \(_stdlib_getTypeName(myvar0))")
println( "TypeName1 = \(_stdlib_getTypeName(myvar1))")
println( "TypeName2 = \(_stdlib_getTypeName(myvar2))")
println( "TypeName3 = \(_stdlib_getTypeName(myvar3))")

Вихід:

TypeName0 = NSString
TypeName1 = _TtC13__lldb_expr_014PureSwiftClass
TypeName2 = _TtSi
TypeName3 = _TtSS

Запис у блозі Юана Свіка допомагає розшифрувати ці рядки:

наприклад, _TtSiозначає внутрішній Intтип Свіфта .

Майк Еш має чудовий запис у блозі, що висвітлює ту саму тему .


@aleclarson Так, це теж досить корисно.
Клаас

1
це не приватний API? Чи затвердить Apple додаток, якщо вони використовуються?
Едуардо Коста

@EduardoCosta так, точно. Вони приватні. Я використовую їх лише для налагодження.
Клаас

Ось оновлене посилання на статтю в блозі Евана Свіка
RenniePet

5

Можливо, ви хочете замість цього використати toString () . Він є загальнодоступним і працює так само, як _stdlib_getTypeName () з тією різницею, що він також працює на AnyClass , наприклад, введіть на дитячу площадку

class MyClass {}

toString(MyClass.self) // evaluates to "__lldb_expr_49.MyClass"

1

У reflectSwift 5 немає ключового слова, тепер його можна використовувати

struct Person {
    var name="name"
    var age = 15
}

var me = Person()
var mirror = Mirror(reflecting: me)

for case let (label?, value) in mirror.children {
    print (label, value)
}

Чому це не схвалюється? Це корисно v. Я збираюся застосувати його для jsonдесеріалізації
javadba
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.