【问题标题】:Does Swift support reflection?Swift 支持反射吗?
【发布时间】:2014-06-05 12:48:16
【问题描述】:

Swift 支持反射吗?例如Swift 对象有类似 valueForKeyPath:setValue:forKeyPath: 的东西吗?

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

【问题讨论】:

  • 我为 Swift 中的反射创建了一个辅助类。您可以在以下位置找到它:github.com/evermeer/EVReflection
  • 他们在 Swift 2.0 中移除了反射。这就是我枚举属性和值的方式Link

标签: ios reflection swift


【解决方案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

【讨论】:

  • 好吧,我不认为这是真正的反映。一方面,它是只读的。在我看来,这只是在 Xcode 中启用调试的一种技巧。协议Mirror实际上多次引用了IDE这个词。
  • 它只适用于属性。没有方法反射。
  • 本文要点的作者。我在 WWDC 的 Swift 实验室写了这篇文章,我想我会分享剩下的部分。正如每个人都知道的那样,与我交谈的工程师证实了 reflect() 函数的存在是为了支持 Playground。但是您仍然可以从中获得一些乐趣:) 在这里,我使用它破解了一个小模型序列化程序。将其粘贴到 Playground 中并玩得开心:gist.github.com/mchambers/67640d9c3e2bcffbb1e2
  • 查看stackoverflow.com/a/25345461/292145 的答案,了解_stdlib_getTypeName 如何提供帮助。
  • 这是一个反射基类和可选项(不是类型)的类,并且支持 NSCoding 和字典解析:github.com/evermeer/EVCloudKitDao/blob/master/AppMessage/…
【解决方案2】:

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

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

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

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

当不扩展 NSObject 或使用 '@objc' 指令时,Swift 默认使用基于静态和 vtable 的调度。这样比较快,但是,在没有虚拟机的情况下不允许运行时方法拦截。这种拦截是 Cocoa 的基本部分,并且是以下类型的功能所必需的:

  • Cocoa 优雅的属性观察者。 (属性观察器直接融入了 Swift 语言)。
  • 以非侵入方式应用横切关注点,例如日志记录、事务管理(即面向方面的编程)。
  • 代理、消息转发等

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

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

总结:

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

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

  • 静态:
  • vtable : ~ 1.1ns
  • 动态:~4.9ns

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

此外,动态属性允许我们显式地指示 Swift 方法应该使用动态调度,因此将支持拦截。

public dynamic func foobar() -> AnyObject {
}

【讨论】:

  • 即使使用 Objective-C 技术,它似乎也不适用于可选的 Swift 类型。我建议在答案中注意这个限制,除非我错过了一个技巧。
【解决方案3】:

文档谈到动态类型系统,主要是关于

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

【讨论】:

    【解决方案4】:

    目前看来,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 有助于破译这些字符串:

    例如_TtSi 代表 Swift 内部的 Int 类型。

    Mike Ash has a great blog entry covering the same topic.

    【讨论】:

    • @aleclarson 是的,这也很有用。
    • 那些不是私有API吗?如果使用该应用程序,Apple 会批准吗?
    • @EduardoCosta 是的,当然。他们是私人的。我只将它们用于调试构建。
    • 这里是 Ewan Swick 博客文章的更新链接:eswick.com/2014/06/08/Inside-Swift
    【解决方案5】:

    您可能要考虑改用 toString()。它是公共的,与 _stdlib_getTypeName() 的工作方式相同,不同之处在于它也适用于 AnyClass,例如在游乐场输入

    class MyClass {}
    
    toString(MyClass.self) // evaluates to "__lldb_expr_49.MyClass"
    

    【讨论】:

      【解决方案6】:

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

      【讨论】:

      • 为什么不赞成?这很有用。我要申请json反序列化
      猜你喜欢
      • 2012-09-26
      • 1970-01-01
      • 1970-01-01
      • 2017-08-13
      • 2020-06-23
      • 2015-12-30
      • 1970-01-01
      • 2021-06-11
      • 2018-06-01
      相关资源
      最近更新 更多