Интроспекция и дженерики Swift

Я пытаюсь динамически создавать тип на основе экземпляра class с использованием дженериков, однако я сталкиваюсь с трудностями с интроспекцией класса.

Вот вопросы:

  • Существует ли Swift-эквивалент Obj-C self.class?
  • Есть ли способ создать экземпляр класса с помощью результата AnyClass из NSClassFromString?
  • Есть ли способ получить AnyClass или иначе ввести информацию строго из общего параметра T? (Подобно синтаксису С# typeof(T))

Ответ 1

Ну, для одного, эквивалент Swift [NSString class] равен .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 дает мне ошибку на игровой площадке.

Ответ 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 name этих классов; это важно, потому что в противном случае мы не знаем строку 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)) также работает.

Ответ 3

Если я правильно прочитал документацию, если вы имеете дело с экземплярами и, например, хотите вернуть новый экземпляр одного и того же типа, чем тот объект, который вам предоставлен, и Тип можно построить с помощью init(), который вы можете сделать:

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

Я быстро проверил его со строкой:

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

который работал нормально.

Ответ 4

В быстро 3

object.dynamicType

устарел.

Вместо этого используйте:

type(of:object)

Ответ 5

Быстрая реализация типов сравнения

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

и bingo, "makeFoo" содержит экземпляр Foo.

Недостатком является то, что ваши классы должны выходить из FactoryObject, и они ДОЛЖНЫ иметь метод инициализации Obj-C +, чтобы ваш класс автоматически включался в карту класса глобальной функцией "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