AVPlayer崩溃问题:Double转Int遇NaN/无穷值,求正确处理方案
问题分析与优化方案
原函数崩溃的核心原因有两个:
- 强制解包
self.player.currentItem?.duration.seconds,即便当前没触发nil崩溃,这也是潜在风险; - 当
duration.seconds为NaN或无限值时,Double(self.value) * 该值的结果同样是NaN/无限,转Int时触发致命错误。
你修改后的版本合理性分析
你的修改思路是对的:先判断totalSeconds是否非nil,再检查是否为NaN/无限,非法时返回0。但存在几个可优化的点:
- 已经把
duration.seconds赋值给totalSeconds,但返回时又重复调用self.player.currentItem?.duration.seconds!,完全没必要,直接用已存储的totalSeconds即可,避免重复解包和潜在的取值不一致问题; - 频繁使用强制解包
totalSeconds!不够优雅,Swift更推荐用可选绑定+guard语句安全处理,减少感叹号的使用。
优化后的代码示例
func getSeconds() -> Int { // 用guard同时完成可选值解包与合法性检查 guard let durationSeconds = self.player.currentItem?.duration.seconds, !durationSeconds.isNaN, !durationSeconds.isInfinite else { // 任何不合法情况(nil、NaN、无限)都返回0或执行自定义错误处理 return 0 } let multiplier = Double(self.value) // 额外检查multiplier合法性(如果self.value存在转Double失败的可能) guard !multiplier.isNaN, !multiplier.isInfinite else { return 0 } let total = multiplier * durationSeconds // 转Int前最后确认结果合法(避免极端场景下乘法溢出导致的异常) guard !total.isNaN, !total.isInfinite else { return 0 } return Int(total) }
关于guard语句的用法说明
guard语句的核心作用是提前退出当前作用域:当条件不满足时,执行else块内的退出逻辑(return、throw等);条件满足时,解包后的变量可在guard之后的代码中直接使用,无需强制解包。
比如上面的代码中,guard let durationSeconds = ... else { return 0 },只要player.currentItem为nil,或duration.seconds是NaN/无限,就直接返回0;否则durationSeconds就是一个安全的非NaN、非无限的Double值,可直接用于后续计算。
简化版本(若无需检查multiplier)
如果能确定self.value转Double不会出现NaN/无限,可简化为:
func getSeconds() -> Int { guard let durationSeconds = self.player.currentItem?.duration.seconds, !durationSeconds.isNaN, !durationSeconds.isInfinite else { return 0 } let total = Double(self.value) * durationSeconds return total.isNaN || total.isInfinite ? 0 : Int(total) }
内容的提问来源于stack exchange,提问作者Eduard Unruh
相关产品推荐
相关产品推荐

