【问题标题】:Pass AnyObject as generic T parameter in Swift 4在 Swift 4 中将 AnyObject 作为通用 T 参数传递
【发布时间】:2018-03-23 14:05:31
【问题描述】:

我有一个有趣的问题:

class ListCache {

    public func getCachedList<T: Codable>() -> [T]? {
        //loads list from file cache using Codable
    }

}

假设我有 Foo 类:

class Foo: Codable {
    var bar = ""
}

现在,我可以这样做:

let array: [Foo] = ListCache().getCachedList()

但我不能这样做:

var listsToLoad: [AnyClass] = [Foo.self]
let clazz = listsToLoad[0]
let array: [Codable] = ListCache().getCachedList()

编译器给我一个错误:

无法显式特化泛型函数

这意味着我不能在循环中调用getCachedList(),因为我必须明确地给它一个类类型。

有没有办法做到这一点?我也尝试过使用泛型类,但我几乎都以同样的方式结束。

编辑:

我尝试过创建:

class CodableClass: Codable {

}

然后:

class Foo: CodableClass {
    //...
}

现在编译器说 clazz 是未声明的:

var listsToLoad: [CodableClass.Type] = [Foo.self]
for clazz in listsToLoad {
    if let array: [clazz] = ListCache().getCachedList() {
        print(array.count)
    }
}

我也试过clazz.Typeclazz.self

【问题讨论】:

  • 为什么你需要在这里使用泛型?你不能只使用public func getCachedList() -&gt; [Codable]?。我真的不明白你在最后一段代码中在做什么。 var listsToLoad: [AnyClass] = [Foo.self] 对我来说没有意义,赋值看起来更像是类型定义?
  • 那是因为在 getCachedList 我从文件加载 json 并使用 Codable 我将它转换为 [T] 数组。
  • 您正在尝试通过let array: [Codable] = ListCache().getCachedList() 创建 Codable 对象数组。但无法构造协议“Codable”,因为它没有可访问的初始化程序。
  • 有没有办法使用与 Codable 不同的东西来使用这样的数组?
  • 另一个建议是:停止自己实现缓存,让URLCache 完成所有工作。在您的请求中,您可以指定缓存策略,您的服务器可以在响应标头中添加Cache-Control 信息。

标签: ios swift generics swift4


【解决方案1】:

我相当肯定,为了调用泛型函数,必须在编译时知道该函数的参数类型。由于[Foo] 实际上只是Array&lt;Foo&gt; 的简写,它也适用于类型化数组(尝试编写Array&lt;clazz&gt;;它不会编译)。因此,当您在 clazz 变量中有一个类型时,使得实际类仅在运行时才知道,您不能使用它来调用泛型函数,也不能声明该类的类型数组。

AFAIK,唯一的解决方案是:

  1. 使用[Codable] 或:

  2. 找到一种方法来动态执行您想要执行的操作,Objective-C 样式
  3. 找到另一种方法来做你想做的事情:-(

由于我不知道您最终想要做什么,因此很难超越这一点,很遗憾。

【讨论】:

  • 在“真实”应用程序中,当应用程序启动时,我从外部服务器下载了几个列表。但是,使用 Codable,我想缓存这些列表,而不是总是下载它们。所以,我想创建一个类数组,将其传递给函数,该函数将从缓存中加载,如果不可能或数据太旧,将从外部服务器下载它。我不想复制粘贴所有代码,而是在数组中很好地做到这一点。
  • @Makalele 您可以为每个数据源定义一个带有大小写的枚举,然后切换枚举,使用适当的静态类型调用getCachedList()。它不像通用代码那么整洁,但它应该可以工作。
  • 我现在这样做的方式相同:我只是对每种类型进行 if-check 而不是枚举。基本上是一样的:)
【解决方案2】:

你可以试试关注

class ListCache {

  public func getCachedList<T: Cachable>() -> [T] {
    return [(Foo() as! T)]
  }

}

protocol Cachable: Codable { }

class CachableClass: Cachable {
  class func test() -> String {
    return "blo"
  }
}

extension Cachable {

  static func cachedValues() -> [Self] {
    return cachedValuesTemplate(type: self)
  }

  private static func cachedValuesTemplate<T: Cachable>(type: T.Type) -> [T] {
    return ListCache().getCachedList()
  }

}

class Foo: CachableClass { }

var listsToLoad: [CachableClass.Type] = [Foo.self]
for clazz in listsToLoad {
  print(clazz.cachedValues().count)
}

【讨论】:

  • 此实现不适用于结构。
  • @Misternewb 我只看到问题中的类。
  • 由于某种原因,在创建 Foo() 项目后,我想更改它的属性(现在我将其命名为 var fastTest = ""),我遇到了 EXC_BAD_ACCESS 崩溃。为什么?
  • 如果我从 Foo 中删除“CachableClass”,则不会发生崩溃。
  • @Makalele 您是否已将return [(Foo() as! T)] 行更改为您的实施?
猜你喜欢
  • 2015-04-16
  • 1970-01-01
  • 1970-01-01
  • 2016-02-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多