Swift中空IndexSet访问startIndex下标返回0是否为预期行为?
Swift 空IndexSet访问startIndex下标不崩溃的原因及特性说明
现象复现
执行以下代码时,和Array访问越界触发崩溃的表现不同,空IndexSet不会崩溃,还会返回值0:
let myIndexSet = IndexSet() let index = myIndexSet[myIndexSet.startIndex] // 运行得到index = 0
行为差异的核心原因
- 按照Swift Collection协议的通用约定,所有空集合的
startIndex都等于endIndex,这一点Array和IndexSet是一致的。两者表现不同完全是下标实现的逻辑差异:- Array的下标访问内置了强制边界校验,只要传入的下标大于等于
endIndex,就会直接触发越界运行时错误,这是Array特意保证的公开行为。 - IndexSet是存储离散整数值的有序集合类型,不是Array这种连续内存的随机访问序列,它的下标接收的是自身自定义的索引类型,不是整数偏移位。当前版本的实现中,IndexSet的下标访问没有做严格的边界校验,当传入等于
endIndex的索引(空集合的startIndex就满足这个条件)时,内部逻辑会直接返回默认值0,这就是你观察到该现象的来源。
- Array的下标访问内置了强制边界校验,只要传入的下标大于等于
是否可以依赖该行为
该行为属于未被官方文档约定的实现细节,绝对不能作为稳定特性依赖。
苹果从未在公开的API说明中承诺空IndexSet访问startIndex会固定返回0,这个表现只是当前版本SDK的内部实现逻辑,后续任何系统版本、Swift版本的更新都有可能调整这个逻辑:比如补上边界校验触发越界崩溃,或者修改默认返回值,一旦底层实现变动,依赖这个隐式行为的业务代码会出现难以排查的线上问题。
如果你的业务场景确实需要“IndexSet为空时取第一个元素默认返回0”的逻辑,建议显式做空判断封装,不要依赖底层隐式行为,参考实现:
extension IndexSet { var firstValueOrZero: Int { isEmpty ? 0 : self[startIndex] } }
这种显式实现的逻辑不受SDK版本变动影响,稳定性有保障。
内容的提问来源于stack exchange,提问作者Remco Poelstra
相关产品推荐
相关产品推荐

