【问题标题】:Cast [AnyObject] to generic type that may or may not be a collection type将 [AnyObject] 转换为可能是也可能不是集合类型的泛型类型
【发布时间】:2016-03-21 11:52:16
【问题描述】:

我有一个AnyObject 数组,我想将它(或一个它的对象)转换为一个泛型类型T,它可能是也可能不是一个数组。

我的第一次尝试:

class MyClass<T> {

    func someMethod() -> T? {

        let anyObjectArray: [AnyObject] = // ... array obtained from Objective-C framework

        if let objectsAsCollection = anyObjectArray as? T {  // Cast always fails
            return objectsAsCollection
        } else if let firstObject = anyObjectArray.first as? T {
            return firstObject
        } else {
            return nil
        }
    }
}

TArray(例如T.self == Array&lt;SomeObject&gt;.self)时,第一个if let 转换失败。

我会尝试这样的事情,但它涉及以某种方式引入另一个泛型参数,所以写的无效:

extension MyClass where T == Array<U> {

    fun someMethod() -> T? {

        let anyObjectArray: [AnyObject] = // ... array obtained from Objective-C framework

        if let objectsAsCollection = anyObjectArray.flatMap({ $0 as? U }) {
            return objectsAsCollection
        } else if let firstObject = anyObjectArray.first as? T {
            return firstObject
        } else {
            return nil
        }
    }
}

想要这样做的原因是我正在使用 Objective-C 框架 (RestKit) 从 REST API 获取结果。我在 Swift 的顶部构建了一个层,它根据发出的请求将这些结果作为特定类型传回,所以我有一个看起来像这样的 Result 类型:

enum Result<T>: {
    case Success(T)
    case Failure(Error)
}

T 有时是单个对象,有时是对象数组,但 Objective-C 框架总是以数组形式返回结果。

【问题讨论】:

  • 如果不限制AnyObject 数组包含多少个不同 类型的对象,这将很困难。例如,假设您的 [AnyObject] 拥有 NSString 以及 NSNumber 实例:如何将它们转换为原生 Swift 类型的数组?请记住,一般来说,使用泛型,比如T,允许不同的类型调用该方法,但是对于每个这样的调用,T 固定为一个单一类型。您确定AnyObject 数组将始终只包含单个相同类型的对象吗?
  • @dfri 从Objective-C 框架返回的AnyObject 数组理论上可以包含任何类型。然而,实际上我知道 REST API 返回一个特定类型的数组,所以对于每个调用 T 要么是 SomeType 要么是 Array&lt;SomeType&gt; (其中 SomeType 取决于返回的对象类型服务器)。
  • 我想我最近正在和其他人讨论完全相同的问题,我们通过将 [AnyObject] 替换为 NSArray 解决了这个问题。

标签: swift generics casting


【解决方案1】:

以下语法将起作用:

class MyClass<T> {
    func someMethod() -> T? {
        // let's not convert to [AnyObject] here, keep it as NSArray
        let anyObjectArray: NSArray = ...

        // For some reason conversion from [AnyObject] to [T] fails...
        // However, conversion from NSArray is special
        if let objectsAsCollection = anyObjectArray as? T {
            return objectsAsCollection
        } else if let firstObject = (anyObjectArray as [AnyObject]).first as? T {
            return firstObject
        } else {
            return nil
        }
    }
}

但是,这只是一个可能的错误的解决方法。我们可以将问题归结为以下几点:

class GenericClass<T> {
    func someMethod() {
        let anyObjectArray: [AnyObject] = ["test1", "test2", "test3"]

        print(T) //Array<String>
        print(T.self == Array<String>.self) //true

        print(anyObjectArray is [String]) //true
        print(anyObjectArray is T) //false - BUG
    }
}

let instance = GenericClass<[String]>()
instance.someMethod()

(举报:SR-1054

这对我来说似乎是一个明显的错误。它实际上看起来像是Type of optionals cannot be inferred correctly in swift 2.2中讨论的另一个错误实例@

【讨论】:

  • 哦,多么简单,谢谢!这不是我在 Objective-C 和 Swift 之间遇到的第一个类型转换的怪癖,但我没想到解决方案会这么简单。你知道为什么NSArray 的演员会受到不同的对待吗?我猜想这与 Objective-C 的动态运行时有关,但我不确定它如何与 Swift 泛型交互......
  • @Stuart 嗯,我以为我知道原因,但我错了。这看起来更像是一种解决方法。我会尝试更多地检查它。就在今天,我们报告了一个转换为泛型类型的错误,所以这可能也是一个错误。
  • @Stuart 我想我有证据证明这是一个错误。
  • 这个证明在我看来相当确凿,特别是因为它没有触及 Obj-C 运行时。感谢您将其添加到您的答案中。我将引用您的示例提交错误报告。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-10-07
  • 1970-01-01
  • 1970-01-01
  • 2018-04-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多