斯威夫特是否支持反思?

时间:2014-06-05 12:48:16

标签: ios reflection swift

Swift支持反思吗?例如是否有类似valueForKeyPath:setValue:forKeyPath:的Swift对象?

实际上它甚至有一个动态类型系统,比如Objective-C中的obj.class

6 个答案:

答案 0 :(得分: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

答案 1 :(得分:44)

如果一个类扩展NSObject,那么Objective-C的所有内省和动态都会起作用。这包括:

  • 向类询问其方法和属性,以及调用方法或设置属性的能力。
  • 交换方法实现的能力。 (为所有实例添加功能)。
  • 能够动态生成和分配新的子类。 (向给定实例添加功能)

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

如果一个类没有扩展NSObject,那么只有新的,非常有限的(并且正在进行中)反射工作(参见reflect / MirrorType),这增加了向实例询问其类和属性的有限能力,但没有上述附加功能。

当不延伸NSObject或使用' @ objc'指令,Swift默认为基于static和vtable的调度。然而,这更快,在没有虚拟机的情况下不允许运行时方法拦截。这种拦截是Cocoa的基本组成部分,是以下类型功能所必需的:

  • 可可优雅的财产观察员。 (财产观察员直接使用Swift语言)。
  • 非侵入性地应用横向关注问题,如日志记录,事务管理(即面向方面编程)。
  • 代理,消息转发等。

因此,它推荐使用Swift实现Cocoa / CocoaTouch应用程序中的clases:

  • 从NSObject扩展。 Xcode中的新类对话引导了这个方向。
  • 如果动态调度的开销导致性能问题,那么可以使用静态调度 - 例如,在调用具有非常小的主体的方法的紧密循环中。

<强>要点:

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

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

  • static:&lt; 1.1ns
  • vtable:~1.1ns
  • 动态:~4.9ns

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

此外,动态属性允许我们明确地指示Swift方法应该使用动态分派,因此将支持拦截。

public dynamic func foobar() -> AnyObject {
}

答案 2 :(得分:8)

文档讲述了一个动态类型系统,主要是关于

TypedynamicType

请参阅Metatype Type (in Language Reference)

示例:

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!"
}

目前,没有实施任何反思。

编辑:我显然错了,请看stevex的回答。内置属性有一些简单的只读反射,可能允许IDE检查对象内容。

答案 3 :(得分:6)

目前似乎Swift反射API并不是Apple的高优先级。但除了@stevex answer之外,标准库中还有另一个有用的功能。

从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's blog entry有助于破译这些字符串:

e.g。 _TtSi代表Swift的内部Int类型。

Mike Ash has a great blog entry covering the same topic

答案 4 :(得分:5)

您可能需要考虑使用 toString()。它是公开的,与 _stdlib_getTypeName()的工作方式相同,区别在于它也适用于 AnyClass ,例如在游乐场输入

class MyClass {}

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

答案 5 :(得分:1)

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