Does Swift support reflection?

Does Swift support reflection? e.g. is there something like valueForKeyPath: and setValue:forKeyPath: for Swift objects?

Actually does it even have a dynamic type system, something like obj.class in Objective-C?

35340 次浏览

文档中提到了动态类型系统,主要是关于

TypedynamicType

参见 元类型类型(在语言引用中)

例如:

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 检查对象内容。

看起来有一些反射支持的开始:

class Fruit {
var name="Apple"
}


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

来自 mChambers 的要点,这里: https://gist.github.com/mchambers/fb9da554898dae3e54f2

如果一个类扩展了 NSObject,那么 Objective-C 的所有内省和动态性都可以工作,其中包括:

  • The ability to ask a class about its methods and properties, and to invoke methods or set properties.
  • 交换方法实现的能力(向所有实例添加功能)。
  • 动态生成和分配新子类的能力(向给定实例添加功能)

此功能的一个缺点是支持 Swift 可选值类型。例如,可以枚举和修改 Int 属性,但 Int?财产不能。可选类型可以部分使用 return/MirrorType 进行枚举,但仍然不能进行修改。

如果一个类没有扩展 NSObject,那么只有新的,非常有限的(和在进行中?)反射可以工作(请参见反射/MirrorType) ,它增加了向实例询问其类和属性的有限能力,但没有上述任何附加特性。

当不扩展 NSObject 或使用“@objecc”指令时,Swift 默认使用基于静态和 vtable 的分派。但是,在没有虚拟机的情况下,这样做会更快,因为虚拟机不允许运行时方法拦截。这种拦截是 Cocoa 的一个基本组成部分,需要以下类型的特性:

  • Cocoa 优雅的属性观察器。
  • 非侵入性地应用横切关注点,如日志记录、事务管理(即面向方面编程)。
  • 代理、消息转发等。

因此,它建议用 Swift 实现 Cocoa/CocoaTouch 应用程序中的类:

  • Extend from NSObject. The new class dialog in Xcode steers in this direction.
  • 如果克劳斯·福尔曼的开销导致性能问题,那么可以使用静态分派——例如,在紧密循环中调用具有非常小的主体的方法。

摘要:

  • Swift 的行为类似于 C + + ,具有快速的静态/vtable 分派和有限的反射。这使得它适合于低级别或性能密集型应用程序,但是没有与 C + + 相关的复杂性、学习曲线或错误风险
  • 虽然 Swift 是一个编译语言,但方法调用的消息传递风格增加了 Ruby 和 Python 等现代语言的自省和动态性,就像 Objective-C 一样,但没有 Objective-C 的遗留语法。

引用数据: 方法调用的执行开销:

  • 静态: < 1.1 ns
  • Vtable: ~ 1.1 ns
  • 动态: ~ 4.9 ns

(实际性能取决于硬件,但比例将保持相似)。

此外,动态属性允许我们明确地指示 Swift,一个方法应该使用克劳斯·福尔曼,因此将支持拦截。

public dynamic func foobar() -> AnyObject {
}

目前,Swift 反射 API 似乎并不是苹果的首要任务。但是除了@stevex 回答之外,标准库中还有另一个函数可以提供帮助。

从 beta 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类型。

Mike Ash 有一个很棒的博客,内容涵盖了同样的主题。

您可能需要考虑改用 ToString ()。它是公共的,和 _stdlib_getTypeName()的工作方式一样,只是它的不同之处在于它也可以在 任何课程上工作,例如在 Playground 输入中

class MyClass {}


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

在 Swift 5中没有 reflect关键字,现在你可以使用

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