Интроспекция и дженерики 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