Поддерживает ли Swift рефлексию?

113

Поддерживает ли Swift рефлексию? например, есть ли что-то подобное valueForKeyPath:и setValue:forKeyPath:для объектов Swift?

На самом деле, есть ли в нем даже система динамических типов, как obj.classв Objective-C?

Хан Нгуен
источник
1
Я создал вспомогательный класс для отражения в Swift. Вы можете найти его по адресу: 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несколько раз.
Sulthan
7
И это работает только для свойств. Нет отражения метода.
Sulthan
11
Автор проверяет суть. Я написал это в лаборатории Swift на WWDC, решил, что поделюсь остальным. Как все уже догадались, инженеры, с которыми я разговаривал, подтвердили, что функция Reflective () существует для поддержки Playground. Но вы все равно можете повеселиться с ним :) здесь я взломал небольшой сериализатор моделей, используя его. Вставьте его в Playground и получайте удовольствие: gist.github.com/mchambers/67640d9c3e2bcffbb1e2
Marc Chambers
1
Посмотрите ответ stackoverflow.com/a/25345461/292145, чтобы узнать, как вам _stdlib_getTypeNameпомочь.
Klaas
1
Вот класс, который будет отображать базовые классы и дополнительные (не типы) и поддерживает NSCoding и синтаксический анализ словаря и словаря: github.com/evermeer/EVCloudKitDao/blob/master/AppMessage/…
Эдвин Вермеер,
44

Если класс расширяется NSObject, тогда весь самоанализ и динамизм Objective-C работают. Это включает:

  • Возможность спрашивать класс о его методах и свойствах, а также вызывать методы или устанавливать свойства.
  • Возможность обмена реализациями методов. (добавить функциональность всем экземплярам).
  • Возможность генерировать и назначать новый подкласс на лету. (добавить функциональность в данный экземпляр)

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

Если класс не расширяется NSObject, то работает только новое, очень ограниченное (и в процессе?) Отражение (см. «Reflection / MirrorType»), которое добавляет ограниченную возможность запрашивать экземпляр о его классе и свойствах, но ни одной из дополнительных функций выше .

Если не расширять NSObject или не использовать директиву @objc, Swift по умолчанию использует отправку на основе статических и виртуальных таблиц. Это быстрее, однако при отсутствии виртуальной машины не допускается перехват метода среды выполнения. Этот перехват является фундаментальной частью Какао и требуется для следующих типов функций:

  • Элегантные наблюдатели за собственностью какао. (Наблюдатели за собственностью запекаются прямо на языке Swift).
  • Неинвазивное применение сквозных задач, таких как ведение журнала, управление транзакциями (например, аспектно-ориентированное программирование).
  • Прокси, пересылка сообщений и т. Д.

Поэтому рекомендуется, чтобы clases в приложениях Cocoa / CocoaTouch, реализованных с помощью Swift:

  • Расширить от NSObject. Диалог нового класса в Xcode движется в этом направлении.
  • Там, где накладные расходы на динамическую отправку приводят к проблемам с производительностью, можно использовать статическую отправку - например, в жестких циклах с вызовами методов с очень маленькими телами.

Резюме:

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

Справочные данные: накладные расходы на выполнение для вызовов методов:

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

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

Кроме того, атрибут dynamic позволяет нам явно указать Swift, что метод должен использовать динамическую отправку и, следовательно, будет поддерживать перехват.

public dynamic func foobar() -> AnyObject {
}
Джаспер Блюз
источник
2
Даже с использованием методов Objective-C он, похоже, не работает для дополнительных типов Swift. Я бы посоветовал отметить это ограничение в ответе, если я не упускаю какой-то трюк.
whitneyland
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!"
}

В настоящее время рефлексия не реализована.

РЕДАКТИРОВАТЬ: Я был явно неправ, см. Ответ Стивекса. Существует простое отражение только для чтения для встроенных свойств, возможно, чтобы позволить IDE проверять содержимое объекта.

Sulthan
источник
6

Кажется, что API отражения 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тип Swift .

У Майка Эша есть отличная запись в блоге, посвященная той же теме .

Клаас
источник
@aleclarson Да, это тоже весьма полезно.
Клаас
1
разве это не частный API? Утвердит ли Apple приложения, если они будут использоваться?
Эдуардо Коста
@EduardoCosta да, конечно. Они частные. Я использую их только для отладочных сборок.
Klaas
Вот обновленная ссылка на статью в блоге Эвана Свика: eswick.com/2014/06/08/Inside-Swift
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)
}

Jacky
источник
Почему за это не проголосовали? Это очень полезно. Я собираюсь применить его для jsonдесериализации
javadba