Поддерживает ли Swift отражение?
Поддерживает ли Swift отражение? например есть ли что-то вроде valueForKeyPath:
и setValue:forKeyPath:
для объектов Swift?
На самом деле у него даже есть система динамического типа, что-то вроде obj.class
в Objective-C?
Ответы
Ответ 1
Похоже, что начало некоторой поддержки отражения:
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
Ответ 2
Если класс расширяет NSObject
, то работает все интроспекция и динамизм Objective-C. Это включает в себя:
- Возможность спросить класс о его методах и свойствах, а также вызвать методы или установить свойства.
- Возможность обмена реализациями метода. (добавьте функциональность ко всем экземплярам).
- Возможность генерировать и назначать новый подкласс на лету. (добавить функциональность к данному экземпляру)
Одним из недостатков этой функции является поддержка дополнительных типов значений Swift. Например, свойства Int могут быть перечислены и изменены, но Int? свойства не могут. Необязательные типы могут быть перечислены частично с использованием mirror/MirrorType, но все еще не изменены.
Если класс не расширяет NSObject
, то работает только новое, очень ограниченное (и в процессе?) отражение (см. отражение /MirrorType ), что добавляет ограниченную способность запрашивать экземпляр о его классе и свойствах, но ни одна из дополнительных функций выше.
Если не расширять NSObject или использовать директиву "@objc", по умолчанию Swift используется для отправки по статической и виртуальной таблицам. Однако это происходит быстрее, но при отсутствии виртуальной машины не разрешается перехват метода времени исполнения. Этот перехват является фундаментальной частью Cocoa и требуется для следующих типов функций:
- Cocoa элегантные наблюдатели свойств. (Собственные наблюдатели запекаются прямо на языке Swift).
- Неинвазивно применяя сквозные проблемы, такие как ведение журнала, управление транзакциями (например, ориентированное на аспектное программирование).
- Прокси, пересылка сообщений и т.д.
Поэтому рекомендуется, чтобы классы в приложениях Cocoa/CocoaTouch, реализованных с помощью Swift:
- Выполнить из NSObject. Диалог нового класса в Xcode направляется в этом направлении.
- В тех случаях, когда накладные расходы на динамическую отправку приводят к проблемам с производительностью, можно использовать статическую отправку - например, в узких петлях с вызовами методов с очень маленькими телами.
Резюме:
- Swift может вести себя как С++, с быстрой отправкой static/vtable и ограниченным отражением. Это делает его подходящим для приложений с более низким уровнем производительности или высокой производительности, но без сложности, кривой обучения или риска ошибки, связанной с С++
- В то время как Swift - это скомпилированный язык, стиль обмена сообщениями вызова метода добавляет интроспекцию и динамизм, найденные на современных языках, таких как Ruby и Python, как и Objective-C, но без Objective-C устаревшего синтаксиса.
Справочные данные: служебные данные выполнения для вызовов методов:
- static: < 1.1нс
- vtable: ~ 1.1ns
- dynamic: ~ 4.9ns
(фактическая производительность зависит от аппаратного обеспечения, но коэффициенты останутся похожими).
Кроме того, динамический атрибут позволяет нам явно проинструктировать Swift о том, что метод должен использовать динамическую отправку и, следовательно, поддерживать перехват.
public dynamic func foobar() -> AnyObject {
}
Ответ 3
Документация говорит о системе динамического типа, в основном о
Type
и dynamicType
См. Тип метатипа (в справочнике по языку)
Пример:
var clazz = TestObject.self
var instance: TestObject = clazz()
var type = instance.dynamicType
println("Type: \(type)") //Unfortunately this prints only "Type: Metatype"
Теперь предположим, что TestObject
extends NSObject
var clazz: NSObject.Type = TestObject.self
var instance : NSObject = clazz()
if let testObject = instance as? TestObject {
println("yes!") //prints "yes!"
}
В настоящее время не реализовано отражение.
РЕДАКТИРОВАТЬ: Я, по-видимому, ошибался, см. stevex ответ. Существует некоторое простое отображение readonly для построения свойств, возможно, чтобы IDE могли проверять содержимое объекта.
Ответ 4
Похоже, что API Swift для отражения в настоящее время не является для Apple приоритетом. Но помимо @stevex answer есть другая функция в стандартной библиотеке, которая помогает.
Начиная с бета-версии 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
Запись в блоге Ewan Swick помогает расшифровать эти строки:
например. _TtSi
обозначает внутренний тип Swift Int
.
У Майка Эша отличная запись в блоге, посвященная той же теме.
Ответ 5
Вместо этого вы можете использовать toString(). Он является общедоступным и работает так же, как _stdlib_getTypeName() с той разницей, что он также работает на AnyClass, например. на игровой площадке введите
class MyClass {}
toString(MyClass.self) // evaluates to "__lldb_expr_49.MyClass"