简体   繁体   中英

Downcasting Multidimensional Arrays Swift

I am trying to downcast a multidimensional array property which is required by a protocol in a subclass of the protocol conforming class. However currently the compiler is giving me a an error when I do so. The error is: 'DataClass' is not identical to 'Any' .

The strange thing is that when the property is reduced to a single dimensional array the error disappears. Could this be a bug with Swift or am I not understanding how Swift handles the typing of multidimensional arrays?

This has been around since Swift 1.0, so I feel like I am missing something obvious here...

I have reproduced my situation in an easily testable code snippet:

protocol MyProtocol {    
    var myProperty: ([[Any]])! { get set }

    func myFuncReturn() -> Any
    func myFuncParam(param: Any)
}

class MyClass: MyProtocol {    
    var myProperty: ([[Any]])!

    init(myProperty: ([[Any]])!) {
        self.myProperty = myProperty
    }

    func myFuncReturn() -> Any {
        return myProperty[0][0]
    }

    func myFuncParam(param: Any) { }
}

class MySubclass: MyClass {

    var myPropertyOver: ([[DataClass]])! {
    return myProperty as? ([[DataClass]])
    }

    init() {
        super.init(myProperty: [[DataClass()]])
    }
}

class DataClass { }

Thanks for your help!

The problem is merely that you aren't being specific enough about what should happen. Let's say you've got an array of arrays and some are of DataClass and some are not. Now what? You need to explain that in your code.

When you cast, say, an [AnyObject] to [Int] , that is just a shorthand. What really happens is that Swift cycles through the whole array and tests whether each every element can be cast to an Int.

But the thing you want to do is something for which there is no shorthand. Indeed, it is far from obvious to me (and therefore to Swift) what you do want to do. You have to be explicit .

Look at it this way. We can reduce the entire example to this simple case:

var arr = [[AnyObject]]()
arr = [[1],[2]]
var arr2 = [[AnyObject]]()
arr2 = [[1],["howdy"]]

Now I am guessing that what you want is this: If every array in the given array is an array that can be cast down to [Int] , do it. If not, then you should end up with nil: the attempt failed.

Okay, so if that's what you want, that's what you have to do . There is no magic shorthand for it. So, for example:

func cast(array:[[AnyObject]]) -> [[Int]]? {
    let result = array.map {
        (subarr:[AnyObject]) -> [Int]? in
        if let subarr = subarr as? [Int] {
            return subarr
        } else {
            return nil
        }
    }
    var foundnil = false
    for subarr in result {
        if subarr == nil {
            foundnil = true
            break
        }
    }
    if foundnil {
        return nil
    }
    return result.map{$0!}
}

And here's proof that it's working properly:

cast(arr) // [[1], [2]]
cast(arr2) // nil

If I haven't understood what output you expect, then just change the cast function to get the output you expect. But that's the whole point; it is not at all clear what you want. You cannot just throw a cast at this thing and expect that to be meaningful, or for Swift to read your mind. You must say what you want.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM