【问题标题】:Storing a type in a variable?将类型存储在变量中?
【发布时间】:2018-09-23 21:40:50
【问题描述】:

所以我想知道是否可以将类型保存在变量中,以便稍后在同一个函数中使用。

我的代码如下:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    guard let object = array.item(at: indexPath.row) else {
        fatalError("Could not retrieve the table object")
    }

    if object.displayOnLeft {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: LeftSidedCell.className, for: indexPath) as? LeftSidedCell else {
            fatalError("Could not dequeue the left sided cell")
        }
        cell.object = object
        return cell
    } else {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: RightSidedCell.className, for: indexPath) as? RightSidedCell else {
             fatalError("Could not dequeue the right sided cell")
        }
        cell.object = object
        return cell
    }
}

但我认为这实际上很笨拙,因为 if 和 else 中的内容本质上是相同的,只是单元格的类型不同。

我理想的算法基本上是:

var type
if object.displayOnLeft {
   type = LeftSidedCell
} else {
   type = RightSidedCell
}

//Dequeue Cell with type

我知道这里有几个问题,首先,我不能在没有类型的情况下声明一个变量,而且我一开始不确定将它分配给什么类型。其次,检索类的类型也没有取得成果。

我已经尝试将它作为属性存储在对象中,以及在我的自定义对象中的函数中返回它,以及以下的一些变体:

func getTypeOfCell<T:TranslationCellProtocol>() -> T.Type {
    if displayOnLeft {
        return LeftSidedCell.self
    } else {
        return RightSidedCell.self
    }
}

无论如何,我不确定这是否可能,或者我是否对某个概念有根本的误解,但我们将不胜感激。

谢谢。

【问题讨论】:

  • 简单的一种衬线解决方案是let type = object.displayOnLeft ? LeftSidedCell.self : RightSidedCell.self,然后使用type.className
  • @Kamran 虽然您的解决方案可能是最简单的解决方案,但编译器不允许我使用 type.className 进行强制转换

标签: ios swift types


【解决方案1】:

只要 LeftSidedCell 和 RightSidedCell 有一个共同的超类——比如 MySidedCell——你就可以将它们存储在一个 MySidedCell.Type 类型的变量中:

let cellType : MySidedCell.Type = LeftSidedCell.self

稍后您可以使用 == 测试该值以查看它是哪种单元格类型。

if cellType == LeftSidedCell.self {

【讨论】:

  • 然而,尽管这很容易实现,但我认为这是一种错误的做法。在 Swift 中对元类型的操作几乎总是一种恶臭。
【解决方案2】:

请注意,您正在使用如此多的可选检查和fataError 调用来阻塞您的代码。在调用cellForRow 时,您应该已经确定您的数据源是正确的。因此,此时无需选择性地打开数据和表格视图单元格,因为如果数据不正确,应用程序无论如何都会崩溃。

例如在调用 numberOfRowsInSection 之前检查您的数组是否没有项目或项目在某种程度上无效,如果您的数据不正确,则返回 0 个项目,cellForRow 将不会被该错误数据调用。

关于代码清洁度,这对我有用:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let object = array[indexPath.row]
    let cell: UITableViewCell

    if object.displayOnLeft {
        cell = tableView.dequeueReusableCell(withIdentifier: "LeftSidedCellIdentifier", for: indexPath) as! LeftSidedCell
    } else {
        cell = tableView.dequeueReusableCell(withIdentifier: "RightSidedCellIdentifier", for: indexPath) as! RightSidedCell
    }

    cell.object = object
    return cell
}

【讨论】:

  • 是的,就许多守卫和致命错误而言,这是一个公平的观点,在某个点之后,我们应该知道代码将起作用。我通常首先设置这样的东西,以确保一切正常,因为如果他们不这样做,我的应用程序将崩溃并且我知道原因,但我仍然通常使用守卫来使单元格出列和类似的东西,所以感谢您对此有所了解。关于这个问题,它或多或少变成了“有人怎么能这样做”与使用if语句并写两次的问题。
【解决方案3】:

首先,编写一个实现className() 方法(或您将用于两个单元类的任何方法和属性)的协议,并让您的两个单元类都符合它

protocol YourCellProtocol {
    var object : YourObjectClass? { get set }
    static func className() -> String
}

class LeftSidedCell : YourCellProtocol {...)
class RightSidedCell : YourCellProtocol {...}

然后你就可以做类似的事情了

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    guard let object = array.item(at: indexPath.row) else {
        fatalError("Could not retrieve the table object")
    }

    var type : YourCellProtocol.Type
    if object.displayOnLeft {
        type = LeftSidedCell.self
    } else {
        type = RightSidedCell.self
    }

    let cell = tableView.dequeueReusableCell(withIdentifier: type.className, for: indexPath)
    if let yourCell = cell as? YourCellProtocol
    {
        yourCell.object = object
    }

    return cell
}

【讨论】:

  • 很棒的东西。谢谢!我确实改变了一件事,我不再使用 if else 来检查 displayOnLeft,我只是使用 className (与重用标识符相同)将一个单元格出列,然后完全像你一样将它嵌入对象做了。
猜你喜欢
  • 1970-01-01
  • 2012-07-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-05
相关资源
最近更新 更多