如何在Swift中捕获"索引超出范围"?

Mat*_*zuk 32 uitableview ios swift

我真的想在我的Swift代码中使用更简单的经典try catch块,但我找不到任何可以做到的东西.

我只需要:

try {
// some code that causes a crash.
}
catch {
// okay well that crashed, so lets ignore this block and move on.
}  
Run Code Online (Sandbox Code Playgroud)

这是我的困境,当TableView重新加载新数据时,一些信息仍然存在于RAM中,它调用didEndDisplayingCell一个带有新空数据源的tableView崩溃.

所以我经常抛出异常 Index out of range

我试过这个:

func tableView(tableView: UITableView, didEndDisplayingCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {

    do {
        let imageMessageBody = msgSections[indexPath.section].msg[indexPath.row] as? ImageMessageBody
        let cell = tableView.dequeueReusableCellWithIdentifier("ImageUploadCell", forIndexPath: indexPath) as! ImageCell
        cell.willEndDisplayingCell()
    } catch {
        print("Swift try catch is confusing...")
    }
}
Run Code Online (Sandbox Code Playgroud)

我也试过这个:

func tableView(tableView: UITableView, didEndDisplayingCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
    print(indexPath.section)
    print(indexPath.row)

    if msgSections.count != 0 {
        if let msg = msgSections[indexPath.section].msg[indexPath.row] as? ImageMessageBody {
            let cell = tableView.dequeueReusableCellWithIdentifier("ImageUploadCell", forIndexPath: indexPath) as! ImageCell
            cell.willEndDisplayingCell()
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这是一个非常低优先级的代码块,我浪费了大量的时间进行试验和错误,弄清楚swift中构建的哪个错误处理程序适用于看起来非常独特的情况,当我有大量的场景就像这样的情况代码可能崩溃,它不会对用户体验产生任何影响.

简而言之,我不需要任何花哨的东西,但Swift似乎有非常具体的错误处理程序,这些处理程序根据我是从函数返回值获取值还是从数组索引中获取可能不存在的值而有所不同.

像其他流行的编程语言一样,是否有一个简单的尝试捕获Swift?

Pen*_*esh 46

正如评论和其他答案中所建议的那样,最好避免这种情况.但是,在某些情况下,您可能需要检查数组中是否存在项目以及是否确实安全地返回该项目.为此,您可以使用以下Array扩展来安全返回数组项.

斯威夫特3

extension Collection where Indices.Iterator.Element == Index {
    subscript (safe index: Index) -> Generator.Element? {
        return indices.contains(index) ? self[index] : nil
    }
}
Run Code Online (Sandbox Code Playgroud)

斯威夫特2

extension Array {
    subscript (safe index: Int) -> Element? {
        return indices ~= index ? self[index] : nil
    }
}
Run Code Online (Sandbox Code Playgroud)
  • 这样你永远不会打 Index out of range
  • 你必须检查项目是否是 nil

是指这个问题更多


在Xcode 8.3.2中的Playground中尝试Swift3代码仍然会导致"崩溃",当我让ar = [1,3,4],然后让v = ar [5].为什么? - 托马斯·坦普尔曼5月17日17:40

你必须使用我们的自定义下标,而不是let v = ar[5],它将是let v = ar[safe: 5].

默认从数组中获取值.

let boo = foo[index]
Run Code Online (Sandbox Code Playgroud)

添加使用自定义下标.

let boo = fee[safe: index]

// And we can warp the result using guard to keep the code going without throwing the exception.
guard let boo = foo[safe: index] else {
  return
}
Run Code Online (Sandbox Code Playgroud)

  • @Sulthan,超出索引可能不是由于编程错误.例如,您正在处理作为程序员无法控制的动态数据.语言应该保护程序永远不会以胎儿运行时错误结束.与其他语言相比,这是Swift糟糕的语言设计和初期的标志.希望在未来版本中这会改变. (5认同)
  • 我希望这是默认行为 (4认同)
  • 当不需要任何选项时,这会向上下文引入选项.最好的解决方案是让您的应用崩溃.索引越界是程序员错误的标志.你不应该忽略那个标志或尝试用可选项吞下它.避免它. (3认同)

jtb*_*des 27

Swift的错误处理(do/ try/ catch)不是像"索引超出范围"这样的运行时异常的解决方案.

运行时异常(您可能还会看到这些称为陷阱,致命错误,断言失败等)是程序员错误的标志.除了在-Ounchecked构建版本中,Swift通常会保证它们会使程序崩溃,而不是继续以错误/未定义的状态执行.这些类型的崩溃可能来自强制解包!,隐式解包,滥用unowned引用,溢出的整数操作/转换,fatalError()s和precondition()s和assert()s等.(不幸的是,Objective-C异常.)

解决方案是简单地避免这些情况.在您的情况下,检查数组的边界:

if indexPath.section < msgSections.count && indexPath.row < msgSections[indexPath.section].msg.count {
    let msg = msgSections[indexPath.section].msg[indexPath.row]
    // ...
}
Run Code Online (Sandbox Code Playgroud)

(或者,正如rmaddy在评论中所说 - 调查为什么会出现这个问题!它根本不应该发生.)


Ale*_*ano 17

斯威夫特4:

extension Collection where Indices.Iterator.Element == Index {
    subscript (exist index: Index) -> Iterator.Element? {
        return indices.contains(index) ? self[index] : nil
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

var index :Int = 6 // or whatever number you need
if let _ = myArray[exist: index] {
   // do stuff
}
Run Code Online (Sandbox Code Playgroud)

要么

var index :Int = 6 // or whatever number you need
guard let _ = myArray[exist: index] else { return }
Run Code Online (Sandbox Code Playgroud)