Swift类自省和泛型


121

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

这里是问题:

  • 有一个Swift相当于Obj-C的self.class吗?
  • 有没有办法使用AnyClassfrom 的结果实例化一个类NSClassFromString
  • 有没有一种方法可以AnyClass严格从通用参数获取信息或以其他方式键入信息T?(类似于C#的typeof(T)语法)

2
stackoverflow.com/a/24069875/292145提供了有关Swift反射API的一些提示。
克拉斯2014年

5
Objective-C self.class将会成为self.dynamicType.selfSwift I的信念
Filip Hermans 2014年

1
在实例方法中,这是调用类方法的方法:self.dynamicType.foo()

Answers:


109

那么,对于一个,雨燕等同的[NSString class]IS .self(见元类型的文档,但他们非常薄)。

实际上,NSString.class它甚至都不起作用!您必须使用NSString.self

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

同样,我尝试了快速班...

class MyClass {

}

let MyClassRef = MyClass.self

// ERROR :(
let my_obj = MyClassRef()

嗯...错误说:

游乐场执行失败:错误::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在操场上却给我一个错误。


1
感谢您链接到Metatype文档!我完全忽略了Types的这一方面,do!
Erik

14
您可以使用.Type.Protocol在变量声明中使用,例如let myObject: MyObject.Type = MyObject.self
Sulthan

1
Sulthan:所以MyObject.Type是一个声明,但是MyObject.self是工厂方法(可以调用),而myObject是一个变量,其中包含对工厂方法的引用。调用myObject()将产生类MyObject的实例。如果myObject变量的名称是myObjectFactory,那将是一个更好的示例。
bootchk 2015年

2
@之前required应删除
fujianjin6471 2015年

49

使用方法如下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名称。这很关键,因为否则我们不知道指定每个类的杂碎字符串。

现在我们可以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
    }

10
支持@objc(ClassName)位。我知道该@objc属性,但不知道还可以提供有关类名的提示。
Erik 2014年

1
出色的解决方案在撰写6年后仍然或多或少地起作用。仅有几个未成年人对操场进行了调整:as! NSObject.Type第一排和aClass.init()第二排
Kaji

13

如果我没看错文档,如果您处理实例,例如想要返回与给定对象相同类型的新实例,并且可以使用init()构造类型,则可以执行以下操作:

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

我很快用String测试了它:

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

效果很好。


1
是的,dynamicType我在那工作了。但是,我无法比较类型。真正的大用途是使用泛型,因此我可以在Generic<T>和里面放一些东西if T is Double {...}。看来这不可能是不幸的。
Erik 2014年

1
@SiLo您是否曾经找到一种方法来询问两个对象是否属于同一类?
马特2014年

1
@matt不优雅,不,我没有。但是,我能够创建一个Defaultable类似于C#default关键字的协议,并为诸如String和等类型正确扩展Int。通过添加的通用约束T:Defaultable,我可以检查参数是否通过is T.default()
Erik

1
@SiLo聪明; 我想看看那个代码!我认为这可以避免使用“是”的奇怪限制。我已经针对这些限制以及普遍缺乏类自省提出了一个错误。我最终使用NSStringFromClass比较了字符串,但是当然只对NSObject后代有效。
马特2014年

1
@matt不幸的是,它听起来比实际要聪明得多,因为您仍然必须执行value is String.default()...等操作,而最终只能这样做value is String
埃里克

13

SWIFT 3

object.dynamicType

不推荐使用。

而是使用:

type(of:object)

7

比较类型的快速实现

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

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


1

终于有了工作。它有点懒,但是即使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”自动插入类映射中。


1

这是另一个示例,显示了类层次结构的实现,类似于已接受的答案,已为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

2
如果变量是超类的类型,则此技术无法正常工作。例如,给定var x: NamedItem.Type,如果我分配它x = Folder.Type,则x()返回一个新值NamedItem,而不是Folder。这使该技术对许多应用程序无用。我认为这是一个错误
phatmann 2014年

1
其实你可以做什么,我想你想使用这种技术stackoverflow.com/questions/26290469/...
possen
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.