Class casting dynamically in swift

后端 未结 5 2012
慢半拍i
慢半拍i 2021-02-19 15:11

I am trying to dyanmically cast to a class in Swift. Is this possible? Here is the code I am trying to use:

let stringClass: AnyClass = NSString.self
let anyObje         


        
相关标签:
5条回答
  • 2021-02-19 15:39

    I'm not sure exactly what you're trying to achieve, but here's a working version of your example:

    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)
    
    0 讨论(0)
  • 2021-02-19 15:39

    The x as! Y.Type syntax only works when Y is explicitly stated.

    So, the compiler wants x as! NSString. The compiler crash is a bug, I suggest that you file a report.

    And just think about it for a second, how would that even help you? stringClass is of type AnyClass, and you're force casting an AnyObject which already conforms to AnyClass. You should cast when you have a static type in mind, because casting doesn't really make any sense otherwise.

    If you want to check, however, whether your object's class is a subclass of a particular type, you'd use:

    x is Y.Type

    0 讨论(0)
  • 2021-02-19 15:47

    I'm not seeing what the cast at this point is for. You can write:

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

    The cast does nothing for you in the code that you have shown. viewController is typed as UIViewController, but it is the correct underlying view controller subclass thanks to polymorphism; whatever the class is in the storyboard, that's the class of this instance.

    The only time you need to cast down is when you have to message an instance with a message belonging only to the subclass, and you have not shown any such need at this point in your code.

    0 讨论(0)
  • 2021-02-19 15:51

    Its possible so long as you can provide "a hint" to the compiler about the type of... T. So in the example below one must use : 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>
    

    Why Swift doesn't just allow us to let casted = cast<String>(inputValue) I'll never know.


    One annoying scenerio is when your func has no return value. Then its not always straightford to provide the necessary "hint". Lets look at this example...

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

    The following client code DOES NOT COMPILE. It gives a "Generic parameter 'T' could not be inferred" error.

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

    But you can solve this by providing a "hint" to compiler as follows:

    asyncCast(inputValue) { (casted: String?) in
        print(casted) // Optional("this is a test")
        print(type(of: casted)) // Optional<String>
    }
    
    0 讨论(0)
  • 2021-02-19 16:04

    While there are/will be ways to make this kind of thing work, the Swifty solution (IMO) is to have your desired classes adhere to a protocol that defines the shared behavior you're trying to use, or simply use a super class they have in common

    This allows the dynamism requried (in most cases at least) while still allowing the compile-time checks that prevent run time errors.

    For your example,

    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()
    

    Note that this requires "Any" rather than "AnyObject" since you need to cast to a protocol

    Since you mentioned ViewControllers, I thought this might help:

    static func createViewController<T: UIViewController>(storyboard: String, scene: String) -> T? {
        return  UIStoryboard(name: storyboard, bundle: nil).instantiateViewControllerWithIdentifier(scene) as? T
    }
    
    0 讨论(0)
提交回复
热议问题