类动态快速转换

时间:2016-08-05 14:59:21

标签: swift casting

我正试图在斯威夫特的一个班级进行动态演绎。这可能吗?这是我试图使用的代码:

let stringClass: AnyClass = NSString.self
let anyObject: AnyObject = "foo"
let string = anyObject as! stringClass

代码无法在演员表中编译。这是可能的,如果是这样,为什么正确的语法?

真实用例

这是真正的问题。我试图重构这段代码:

switch (value) {
    case "valueOne":
        viewController = storyboard.instantiateViewController(withIdentifier: "foo") as! FirstViewController
    case "valueTwo":
        viewController = storyboard.instantiateViewController(withIdentifier: "bar") as! SecondViewController
    default:
        return nil
}

成:

let controllersDictionary: [String: (String, UIViewController.Type)] = [
    "valueOne" : ("bar", FirstViewController.self),
    "valueTwo" : ("foo", SecondViewController.self)
]
let tuple = controllersDictionary[value]!
let identifier = tuple.0
let cast = tuple.1
let viewController = storyboard.instantiateViewController(withIdentifier: identifier) as! cast

5 个答案:

答案 0 :(得分:5)

我不确定你想要实现的目标,但这是你的例子的工作版本:

func cast<T>(value: Any, to type: T) -> T? {
    return castedValue as? T
}

let inputValue: Any = "this is a test"
let inputType = String.self()
let casted = cast(value: inputValue, to: inputType)

print(casted)

答案 1 :(得分:2)

我没有看到此时的演员是什么。你可以写:

let controllersDictionary: [String: String] = [
    "valueOne" : "bar",
    "valueTwo" : "foo"
]
let identifier = controllersDictionary[value]!
let viewController = storyboard.instantiateViewController(withIdentifier: identifier)

演员在您显示的代码中没有为您做任何事。 viewController被输入为UIViewController,但由于多态性,它是正确的底层视图控制器子类;无论故事板中的类是什么,这都是这个实例的类。

您需要抛弃的唯一时间是您必须使用仅属于子类的消息向实例发送消息,并且您的代码中此时尚未显示任何此类需求。

答案 2 :(得分:1)

虽然有/将会有办法使这种事情发挥作用,但Swifty解决方案(IMO)是让您所需的类遵循定义您尝试使用的共享行为的协议,或者只是使用他们有共同的超级课程

这允许动态需要(在大多数情况下至少),同时仍然允许编译时检查以防止运行时错误。

对于您的示例,

protocol Stringable {
    func toString() -> String
}

extension String: Stringable {
    func toString() -> String {
        return self
    }
}

let thing = "foo"
let anything: Any = thing
let test: String? = (anything as? Stringable)?.toString()

请注意,这需要“Any”而不是“AnyObject”,因为您需要转换为协议

由于您提到了ViewControllers,我认为这可能有所帮助:

static func createViewController<T: UIViewController>(storyboard: String, scene: String) -> T? {
    return  UIStoryboard(name: storyboard, bundle: nil).instantiateViewControllerWithIdentifier(scene) as? T
}

答案 3 :(得分:0)

x as! Y.Type语法仅在明确声明Y时才有效。

因此,编译器需要x as! NSString。编译器崩溃是一个错误,我建议您提交报告。

只要想一下,这对你有什么帮助? stringClass类型为AnyClass,您强制投射AnyObject 符合AnyClass。当你考虑到静态类型时,你应该进行强制转换,因为否则铸造并没有任何意义。

但是,如果要检查对象的类是否是特定类型的子类,则使用:

x is Y.Type

答案 4 :(得分:0)

有可能,只要您可以向编译器提供有关... T类型的“提示”。因此,在下面的示例中,必须使用: String?

func cast<T>(_ value: Any) -> T? {
    return value as? T
}

let inputValue: Any = "this is a test"
let casted: String? = cast(inputValue) 
print(casted) // Optional("this is a test")
print(type(of: casted)) // Optional<String>

为什么Swift不仅允许我们let casted = cast<String>(inputValue)我永远不会知道。


一个烦人的场景是当您的函数没有返回值时。然后,它不一定总是提供必要的“提示”。让我们来看这个例子...

func asyncCast<T>(_ value: Any, completion: (T?) -> Void) {
    completion(value as? T)
}

以下客户端代码未编译。它给出了“无法推断通用参数'T'”错误。

let inputValue: Any = "this is a test"
asyncCast(inputValue) { casted in
    print(casted) 
    print(type(of: casted))
}

但是您可以通过向编译器提供“提示”来解决此问题,如下所示:

asyncCast(inputValue) { (casted: String?) in
    print(casted) // Optional("this is a test")
    print(type(of: casted)) // Optional<String>
}