How can I declare a variable in Swift using the type defined in an AnyClass object?

邮差的信 提交于 2019-12-08 03:31:37

问题


In my old Obj-C code I could declare a dictionary whose values were the Class types of other classes

NSMutableDictionary *Methods = [[NSMutableDictionary alloc] init];
[Methods setObject:[AuthReturnValue class] forKey:@"Authenticate"];
[Methods setObject:[MyOptions class] forKey:@"GetOptions"];

Later, based off of the key, I could assign that Class to another variable

(in the header)

Class returnType;

(in the implementation):

returnType = (Class)[Methods objectForKey:methodName];

And then I could use this Class variable to declare a new variable of that same type (in this case it's using JSONModel and initializing it with an NSDictionary from elsewhere)

id<NSObject> result;
result = [[returnType alloc] initWithDictionary:(NSDictionary *)responseObject error:NULL];

This was nice and convenient and since JSONModel implements initWithDictionary, meant that I could just pull in the Class in this way without having to instantiate a particular type.

I can't figure out how to do this in Swift.

For example, this doesn't work:

var result: self.returnType.self()
var result: AnyClass = self.returnType.self

And a few dozen variants.

How can I declare a variable in Swift to be the class defined in an AnyClass object? Or am I going about this all wrong?


回答1:


You can't instantiate AnyClass, as far as I know. You must downcast it to a more concrete type. Additionally, the type you want to instantiate with its metatype, must have a required initialiser. If I understood your example, AuthReturnValue and MyOptions are both subclasses of JSONModel, which has init(responseObject:error:) initialiser. Then that initialiser must be required and implemented by every subclass.

class JSONModel {
    required init(responseObject: NSDictionary, error: NSError?) {

    }
}

class AuthReturnValue : JSONModel {
    required init(responseObject: NSDictionary, error: NSError?) {
        super.init(responseObject: responseObject, error: error)
    }
}

class MyOptions : JSONModel {
    required init(responseObject: NSDictionary, error: NSError?) {
        super.init(responseObject: responseObject, error: error)
    }
}

Now you can do something like this:

var methods = [String : JSONModel.Type]()
methods["Authenticate"] = AuthReturnValue.self
methods["GetOptions"] = MyOptions.self
if let returnType = methods["Authenticate"] {
    let result = returnType(responseObject: NSDictionary(), error: nil)
}

UPDATE:

The above code works well with native Swift classes, but crashes currently (Xcode6-Beta6) if used with subclasses of Objective-C classes. The workaround is to store metatype values in a [String : Any.Type] dictionary and downcast before using. The following example shows how to do this with a subclass of NSOperation.

class SomeOperation : NSOperation {

}

var dictionary = [String : Any.Type]()
dictionary["some operation"] = SomeOperation.self

if let aClass = dictionary["some operation"] as? NSOperation.Type {
    // Any initializer available in the superclass can be used for
    // creating instances. The compiler will not perform any checks,
    // as it does with native Swift classes, so we must ensure that subclasses
    // actually implement those initializers, either by automatically inheriting
    // or overriding.
    let test = aClass() 
    println(NSStringFromClass(test.dynamicType))
}



回答2:


I have implemented something similar in a small dependency injection framework, and in the end I figure out that the best way to achieve that is by storing a closure instantiating the object.

This is how I would implement the instantiator class:

typealias Constructor = (responseObject: NSDictionary, error: NSError?) -> AnyObject

class Instantiator {
    private var instantiators = [String : Constructor]()

    func bindKey<T : AnyObject>(key: String, withType type:T.Type, toInitializer initializer: Constructor) {
        self.instantiators[key] = initializer
    }

    func instanceForKey(key: String, responseObject: NSDictionary, error: NSError?) -> AnyObject? {
        if let instantiator = self.instantiators[key] {
            return instantiator(responseObject: responseObject, error: error)
        }

        return .None
    }
}

Then this is how I would use it:

class MyClass {
    let x = "Test"
    init(responseObject: NSDictionary, error: NSError?) {}
}

let instantiator = Instantiator()

instantiator.bindKey("GetOptions", withType: MyClass.self) { (responseObject: NSDictionary, error: NSError?) -> MyClass in
    return MyClass(responseObject: responseObject, error: error)
}

let x: MyClass! = instantiator.instanceForKey("GetOptions", responseObject: NSDictionary(), error: nil) as MyClass?


来源:https://stackoverflow.com/questions/25437519/how-can-i-declare-a-variable-in-swift-using-the-type-defined-in-an-anyclass-obje

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