【问题标题】:generic type from string来自字符串的泛型类型
【发布时间】:2018-03-16 19:49:02
【问题描述】:

编辑:(最初的例子太简单了,所以我重写了代码以更具体)

基于http://holko.pl/2016/01/05/typed-table-view-controller/

我正在尝试查看是否可以从字符串中设置类型的泛型参数..

假设我们有这个代码

protocol Updatable
{
    associatedtype ViewModel
    func updateWith(viewModel: ViewModel)
}

class ToasterCell: UITableViewCell
{
    var toast: String?
    func updateWith(viewModel: String) {
        toast = viewModel
        //Additional config...
    }
}
extension ToasterCell: Updatable
{
    typealias ViewModel = String
}

class PriceCell: UITableViewCell
{
    var tagPrice: Float?
    func updateWith(viewModel: Float) {
        tagPrice = viewModel
        //Additional config
    }
}
extension PriceCell: Updatable
{
    typealias ViewModel = Float
}


protocol CellConfiguratorType {

    var reuseIdentifier: String { get }
    var cellClass: AnyClass { get }

    func updateCell(_ cell: UITableViewCell)
}

class MyTypeTest<Cell> where Cell: Updatable , Cell: UITableViewCell
{
    let viewModel: Cell.ViewModel
    let reuseIdentifier: String = String(describing: Cell.self)
    let cellClass: AnyClass = Cell.self
    init(viewModel: Cell.ViewModel) {
        self.viewModel = viewModel
    }
    func updateCell(_ cell: UITableViewCell)
    {
        if let c = cell as? Cell
        {
            c.updateWith(viewModel: viewModel)
        }
    }
}
extension MyTypeTest: CellConfiguratorType{
}

let myTT1 = MyTypeTest<PriceCell>(viewModel: 3.76)
let myTT2 = MyTypeTest<ToasterCell>(viewModel: "Carpe Diem")
let data = [myTT1, myTT2] as [CellConfiguratorType] // data for the tableView
//register Cell calss ...

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
    let cellConf = data[indexPath.row]
    let cell = tableView.dequeueReusableCell(withIdentifier: cellConf.reuseIdentifier)
    cellConf.updateCell(cell)
    return cell
}

我们希望它的类型 T 是从我们从 JSON 响应中获得的字符串中设置的。

//Some JSON {"list":[{"k":"Price","v":368.0},"{"k":"ToasterCell","v":"YOLO"},"{"k":"Toaster","v":"Space"},{"k":"PriceCell","v":1999}]}

JSON 值不直接映射到任何对象/类,所以我需要使用那个键“k”来知道要使用的女巫类。

我尝试使用“k”值中的字符串来设置单元配置器。

(简短示例)

//for now let skip any logic in decoding the value / viewModel.
let myTT1 = MyTypeTest<NSClassFromString(list.first.k + "Cell")>(viewModel: list.first.v as Any)

我得到的只是以下错误:

  • 无法将类型“T”的值分配给类型“AnyClass”(又名“AnyObject.Type”)
  • 使用未声明的类型“myTypeOBJ”

有没有办法通过字符串来做到这一点,还是我真的需要为我可以从我的 JSON 响应中获得的任何类型创建一个巨大的“if-else”结构?

编辑: 我尝试使用 Cell 类型的参数向 CellConfigurator 添加一个 init,以便它可以从它自己的参数推断类型。

init(viewModel: Cell.ViewModel, inferUsing: Cell){....}

我可以尝试在哪里使用它(但由于 PAT 妨碍了它,所以它不起作用)

func getSafeBundelName() -> String
    {
        if let namespace = Bundle.main.infoDictionary!["CFBundleExecutable"] as? String
        {
            return namespace
        }
        return ""
    }

let cellClass = NSClassFromString("\(getSafeBundelName()).PriceCell") as? UITableViewCell.Type
let cell = cellClass?.init()
let myTT1 = MyTypeTest(viewModel: list.first.v as Any, inferUsing: cell)

我收到无法推断单元类型的错误。如果我尝试在 中使用 cellClass
例如:MyTypeTest&lt;cellType&gt;(viewModel: 3.76) 它给我的只是没有声明“cellClass”。在我看来,我正陷入死胡同,无法以任何我能看到的方式推断 PAT。我觉得这个限制非常非常可悲。

【问题讨论】:

    标签: json swift generics


    【解决方案1】:

    如果将结果从NSClassFromString 向下转换为具有可以调用的已知初始化程序的类型,则可以执行此操作。如果它总是Toaster 或子类,那么你可以这样做:

    if let myTypeOBJ = NSClassFromString("Toaster") as? Toaster.Type {
        let test = myTypeTest(someOBJ: myTypeOBJ.init())
        // test.someThing will be of type Toaster
    }
    

    为了让NSClassFromString 工作,您还需要指定类型名称在 objc 中的表示方式,否则类型名称前面会附加一些其他内容:

    @objc(Toaster)
    class Toaster: NSObject
    

    【讨论】:

    • 如果 myTypeTest 不是通用的,则此方法有效。我需要 myTypeTest 是通用的。类 myTypeTest 其中 T: NSObject
    • 除非你对“作品”有不同的定义
    • myTypeTest(someOBJ: myTypeOBJ.init()) 不是通用的
    • 也就是说,编译器能够通过查看你传入初始化器的对象的类型来推断泛型类型
    • 您需要了解编译时发生的事情和运行时发生的事情之间的区别。泛型需要能够在编译时推断它们的类型,NSClassFromString 在运行时返回一个类。您需要在编译时将 json 中的可能值映射到类型(例如要避免的 if-else 结构)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-05
    • 2011-06-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多