Swift class introspection & generics

前提是你 提交于 2019-12-17 03:22:46

问题


I am trying to dynamically create a class instance based type using generics, however I am encountering difficulty with class introspection.

Here are the questions:

  • Is there a Swift-equivalent to Obj-C's self.class?
  • Is there a way to instantiate a class using the AnyClass result from NSClassFromString?
  • Is there a way to get AnyClass or otherwise type information strictly from a generic parameter T? (Similar to C#'s typeof(T) syntax)

回答1:


Well, for one, the Swift equivalent of [NSString class] is .self (see Metatype docs, though they're pretty thin).

In fact, NSString.class doesn't even work! You have to use NSString.self.

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

Similarly, with a swift class I tried...

class MyClass {

}

let MyClassRef = MyClass.self

// ERROR :(
let my_obj = MyClassRef()

Hmm… the error says:

Playground execution failed: error: :16:1: error: constructing an object of class type 'X' with a metatype value requires an '@required' initializer

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

It took me a while to figure out what this means… turns out it wants the class to have a @required init()

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

    required init () {

    }
}

let Y = X.self

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

Some of the docs refer to this as .Type, but MyClass.Type gives me an error in the playground.




回答2:


Here's how to use NSClassFromString. You have to know the superclass of what you're going to end up with. Here are a superclass-subclass pair that know how to describe themselves for 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"}
}

Notice the use of the special @obj syntax to dictate the Objective-C munged name of these classes; that's crucial, because otherwise we don't know the munged string that designates each class.

Now we can use NSClassFromString to make the Zork class or the Zilk class, because we know we can type it as an NSObject and not crash later:

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

And it's reversible; println(NSStringFromClass(anObject.dynamicType)) also works.




回答3:


If I'm reading the documentation right, if you deal with instances and e.g. want to return a new instance of the same Type than the object you have been given and the Type can be constructed with an init() you can do:

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

I quickly tested it with String:

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

which worked fine.




回答4:


In swift 3

object.dynamicType

is deprecated.

Instead use:

type(of:object)



回答5:


Swift implementation of comparing types

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

NOTE: Notice that it also works with protocols the object may or may not extend




回答6:


Finally got something to work. Its a bit lazy but even the NSClassFromString() route did not work for me...

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

and bingo, "makeFoo" contains a Foo instance.

The downside is your classes must derrive from FactoryObject and they MUST have the Obj-C +initialize method so your class gets automagically inserted in the class map by global function "mapClass".




回答7:


Here is another example showing class hierarchy implementation, similar to accepted answer, updated for the first release of 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()

Prints this result:

base
file
display
base
folder
display


来源:https://stackoverflow.com/questions/24049673/swift-class-introspection-generics

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!