问题
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