简体   繁体   中英

Check if AnyObject is of generic type in Swift

Let's say that I have generic class Bus :

class Bus<T> {
    func doSomething() {}
}

and I can create instance of it:

var myBus = Bus<String>()

Now I have a function that takes one parameter of the AnyObject and tests it's type:

func checkType(object: AnyObject) {
    if let foo = object as? String {
        println("string")
    }
}

My problem is that I can't see a way to check if object is of type Bus and run function doSomething() if it is of type Bus . Any help would be appreciated.


Edit : Protocols also don't seem to solve this the way they should.

import Foundation

@objc protocol BusProtocol {
    func doSomething() -> Void
}

class Bus<T> : BusProtocol {
    func doSomething() -> Void {
        println("asdf")
    }
}

func checkType(object: AnyObject) {
    if let foo = object as? Bus<AnyObject> {
        foo.doSomething() // no match
    }
    if let foo = object as? Bus<Any> {
        foo.doSomething() // no match
    }
    if let foo = object as? Bus<String> {
        foo.doSomething() // prints "asdf"
    }
    if let foo = object as? BusProtocol {
        foo.doSomething() // SIGABRT -- -[SwiftObject doesNotRecognizeSelector:]
    }
}

checkType(Bus<String>())

The problem here is your thinking of Bus as a concrete thing. It really isn't. Bus<String> is. Bus<Int> is too. But Bus isn't, at least not in the same sense. You need to know what T is.

Really, what you want is to write something like this:

func checkType<T>(object: AnyObject) {
    if let foo = object as? Bus<T> {
        println("Bus<T>")
    }
}

But if you try and use it, you'll get an error:

// error: Argument for generic parameter 'T' could not be inferred.
checkType(myBus)

Unlike in other languages, you can't write checkType<String>(myBus) . But the following might do what you're looking for:

func checkType<T>(object: AnyObject, T.Type) {
    if let foo = object as? Bus<T> {
        println("Bus<T>")
    }
}

checkType(myBus,String.self)

This fixes what T is for any Bus<T> and will work correctly.

You might object that you don't want to specify what T is. However, instead, this leads to the question... once you've figured out that object is some kind of Bus , what are you going to do then? Are you planning on calling methods on it, or passing it as an argument to other functions? Chances are what you're trying to achieve can be better done with a generic function and protocol constraints, rather than using AnyObject and casting.

In swift 2.x you can use a protocol to achieve this, as you attempted without error:

protocol Busish {
  func doSomething() -> Void
}

class Bus<T> : Busish {
  func doSomething() {
    print(self)
  }
}

func with_any_bus(obj:AnyObject) {
  if let b = obj as? Busish {
    b.doSomething()
  }
}

with_any_bus(Bus<Int>());
with_any_bus(Bus<String>());

output:

swiftblah.Bus<Swift.Int>
swiftblah.Bus<Swift.String>

This may or may not be helpful to you specifically, since you seem to be using 1.2, but maybe someone else who stumbles on this question will find it useful.

You've practically got it.

func checkType(object: AnyObject) {
    if let foo = object as? Bus<AnyObject> {
        print("Got here")
    } else {
        print("Fail")
    }
}

let bus = Bus<String>()
checkType(bus) // Fail

let otherBus = Bus<AnyObject>()
checkType(otherBus) // "Got Here"

I know that's not really what you want, but it shows what Swift needs.

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