【问题标题】:Swift class introspection & genericsSwift 类内省和泛型
【发布时间】:2014-07-25 19:49:00
【问题描述】:

我正在尝试使用泛型动态创建基于 class 实例的类型,但是在进行类自省时遇到了困难。

以下是问题:

  • 是否有与 Obj-C 的 self.class 等效的 Swift 版本?
  • 有没有办法使用来自NSClassFromStringAnyClass 结果实例化一个类?
  • 有没有办法从泛型参数T 中严格获取AnyClass 或其他类型信息? (类似于 C# 的 typeof(T) 语法)

【问题讨论】:

  • stackoverflow.com/a/24069875/292145 给出了一些关于 Swift 反射 API 的提示。
  • Objective-C 的 self.class 在我相信的 Swift 中会变成 self.dynamicType.self
  • 在实例方法中,调用类方法如下:self.dynamicType.foo()

标签: introspection swift


【解决方案1】:

好吧,首先,[NSString class] 的 Swift 等效项是 .self(请参阅 Metatype docs,尽管它们很薄)。

事实上,NSString.class 甚至不起作用!你必须使用NSString.self

let s = NSString.self
var str = s()
str = "asdf"

同样,我尝试了一个 swift 类...

class MyClass {

}

let MyClassRef = MyClass.self

// ERROR :(
let my_obj = MyClassRef()

嗯……错误说:

Playground 执行失败:错误::16:1:错误:使用元类型值构造类类型“X”的对象需要一个“@required”初始化器

 Y().me()
 ^
 <REPL>:3:7: note: selected implicit initializer with type '()'
 class X {
       ^

我花了一段时间才弄清楚这意味着什么……结果它希望班级有一个@required init()

class X {
    func me() {
        println("asdf")
    }

    required init () {

    }
}

let Y = X.self

// prints "asdf"
Y().me()

一些文档将此称为.Type,但MyClass.Type 在操场上给了我一个错误。

【讨论】:

  • 感谢您提供指向 Metatype 文档的链接!我完全忽略了类型的这一方面,doh!
  • 您可以在变量声明中使用.Type.Protocol,例如let myObject: MyObject.Type = MyObject.self
  • Sulthan:所以 MyObject.Type 是一个声明,但 MyObject.self 是一个工厂方法(可以调用),而 myObject 是一个包含对工厂方法的引用的变量。调用 myObject() 将产生类 MyObject 的实例。如果 myObject 变量的名称是 myObjectFactory 会更好?
  • @ 之前的required 应该被删除
【解决方案2】:

这里是如何使用NSClassFromString。你必须知道你最终会得到什么的超类。这是一对知道如何为println 描述自己的超类-子类:

@objc(Zilk) class Zilk : NSObject {
    override var description : String {return "I am a Zilk"}
}

@objc(Zork) class Zork : Zilk {
    override var description : String {return "I am a Zork"}
}

注意使用特殊的@obj 语法来指示这些类的Objective-C munged 名称;这很关键,否则我们不知道指定每个类的 munged 字符串。

现在我们可以使用NSClassFromString 来创建 Zork 类或 Zilk 类,因为我们知道我们可以将其作为 NSObject 输入,并且以后不会崩溃:

let aClass = NSClassFromString("Zork") as NSObject.Type
let anObject = aClass()
println(anObject) // "I am a Zork"

而且它是可逆的; println(NSStringFromClass(anObject.dynamicType)) 也可以。


现代版:

    if let aClass = NSClassFromString("Zork") as? NSObject.Type {
        let anObject = aClass.init()
        print(anObject) // "I am a Zork"
        print(NSStringFromClass(type(of:anObject))) // Zork
    }

【讨论】:

  • 支持@objc(ClassName) 位。我知道@objc 属性,但不知道你也可以提示类名。
  • 出色的解决方案,在 6 年后仍然或多或少地工作。操场要求的只是几个小调整:as! NSObject.Type 在第一行,aClass.init() 在第二行
【解决方案3】:

如果我正确阅读文档,如果您处理实例,例如想要返回与给定对象相同类型的新实例,并且可以使用 init() 构造类型:

let typeOfObject = aGivenObject.dynamicType
var freshInstance = typeOfObject()

我很快用 String 测试了一下:

let someType = "Fooo".dynamicType
let emptyString = someType()
let threeString = someType("Three")

效果很好。

【讨论】:

  • 是的,dynamicType 按我的预期工作。但是,我一直无法比较类型。真正的大用途是泛型,所以我可以有类似Generic&lt;T&gt; 和内部if T is Double {...} 的东西。不幸的是,这似乎是不可能的。
  • @SiLo 你有没有找到一种方法来概括地询问两个对象是否属于同一类?
  • @matt 不优雅,不,我没有。但是,我能够创建一个类似于 C# 的 default 关键字的 Defaultable 协议,以及像 StringInt 这样的类型的适当扩展。通过添加T:Defaultable 的通用约束,我可以检查参数是否通过is T.default()
  • @SiLo 聪明;我想看看那个代码!我认为这绕过了使用“is”的奇怪限制。我已经就这些限制以及普遍缺乏类内省提出了一个错误。我最终使用 NSStringFromClass 比较字符串,但当然这只适用于 NSObject 后代。
  • @matt 不幸的是,这听起来比实际上更聪明,因为您仍然需要执行 value is String.default()... 等操作,而您最终将改为执行 value is String
【解决方案4】:

swift 3

object.dynamicType

已弃用。

改为使用:

type(of:object)

【讨论】:

    【解决方案5】:

    比较类型的 Swift 实现

    protocol Decoratable{}
    class A:Decoratable{}
    class B:Decoratable{}
    let object:AnyObject = A()
    object.dynamicType is A.Type//true
    object.dynamicType is B.Type//false
    object.dynamicType is Decoratable.Type//true
    

    注意:请注意,它也适用于对象可能扩展或不扩展的协议

    【讨论】:

      【解决方案6】:

      终于找到工作了。它有点懒,但即使是 NSClassFromString() 路线也不适合我......

      import Foundation
      
      var classMap = Dictionary<String, AnyObject>()
      
      func mapClass(name: String, constructor: AnyObject) -> ()
      {
          classMap[name] = constructor;
      }
      
      class Factory
      {
          class func create(className: String) -> AnyObject?
          {
              var something : AnyObject?
      
              var template : FactoryObject? = classMap[className] as? FactoryObject
      
              if (template)
              {
                  let somethingElse : FactoryObject = template!.dynamicType()
      
                  return somethingElse
              }
      
              return nil
          }
      }
      
      
       import ObjectiveC
      
       class FactoryObject : NSObject
      {
          @required init() {}
      //...
      }
      
      class Foo : FactoryObject
      {
          class override func initialize()
          {
              mapClass("LocalData", LocalData())
          }
          init () { super.init() }
      }
      
      var makeFoo : AnyObject? = Factory.create("Foo")
      

      宾果游戏,“makeFoo”包含一个 Foo 实例。

      缺点是您的类必须派生自 FactoryObject,并且它们必须具有 Obj-C +initialize 方法,以便您的类通过全局函数“mapClass”自动插入到类映射中。

      【讨论】:

        【解决方案7】:

        这是另一个显示类层次结构实现的示例,类似于已接受的答案,已针对 Swift 的第一个版本进行了更新。

        class NamedItem : NSObject {
            func display() {
                println("display")
            }
        
            required override init() {
                super.init()
                println("base")
            }
        }
        
        class File : NamedItem {
            required init() {
                super.init()
                println("folder")
            }
        }
        
        class Folder : NamedItem {
            required init() {
                super.init()
                println("file")
            }
        }
        
        let y = Folder.self
        y().display()
        let z = File.self
        z().display()
        

        打印这个结果:

        base
        file
        display
        base
        folder
        display
        

        【讨论】:

        • 如果变量是超类的类型,此技术将无法正常工作。例如,给定var x: NamedItem.Type,如果我将其分配为x = Folder.Type,那么x() 返回一个新的NamedItem,而不是Folder。这使得该技术对许多应用程序毫无用处。我认为这是a bug
        • 其实你可以做我认为你想要使用这种技术stackoverflow.com/questions/26290469/…
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-09-12
        • 1970-01-01
        • 1970-01-01
        • 2016-11-13
        • 2016-04-15
        • 1970-01-01
        相关资源
        最近更新 更多