ジェネリックを使用してclass
インスタンスベースの型を動的に作成しようとしていますが、クラスのイントロスペクションで問題が発生しています。
質問は次のとおりです。
self.class
と同等のSwiftはありますか?AnyClass
からのNSClassFromString
の結果を使用してクラスをインスタンス化する方法はありますか?AnyClass
を取得する方法や、汎用パラメーターT
から厳密に情報を入力する方法はありますか? (C#のtypeof(T)
構文に類似)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()
うーん...エラーは言います:
プレイグラウンドの実行に失敗しました:エラー::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
は遊び場でエラーを出します。
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))
も機能します。
ドキュメントの権利を読んでいる場合、インスタンスを処理している場合、与えられたオブジェクトと同じTypeの新しいインスタンスを返したい場合、Typeはinit()で構築できます:
let typeOfObject = aGivenObject.dynamicType
var freshInstance = typeOfObject()
Stringですばやくテストしました。
let someType = "Fooo".dynamicType
let emptyString = someType()
let threeString = someType("Three")
うまくいきました。
In Swift
object.dynamicType
非推奨です。
代わりに使用します:
type(of:object)
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
注:オブジェクトが拡張する場合としない場合があるプロトコルでも機能することに注意してください
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
ようやく機能するようになりました。少し怠laですが、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から派生する必要があり、グローバル関数「mapClass」によってクラスマップにクラスが自動的に挿入されるように、Obj-C + initializeメソッドを持たなければならないことです。