swift 奇怪的索引设计

这里每天分享一个 iOS 的新知识,快来关注我吧

前言

swift 中有一些比较奇怪的设计,其中切片的索引问题就经常被诟病,今天来详细讲讲。

首先来猜测以下代码输出什么内容:

css 复制代码
var arr = [1, 2, 3, 4]
var subArr = arr[1..<3]
print(subArr[0])

切片索引并非都从 0 开始

公布结果,以上代码会发生越界崩溃。

有些人看到这个结果会比较奇怪,subArr = arr[1..<3] 的结果明明是 2, 3,那 subArr[0] 应该是 2 啊,为什么会越界呢?

其实通过 arr[1..<3] 这种方法取得的是一个 ArraySlice<Element> 类型,也叫数组切片。

看下这个函数的声明:

swift 复制代码
public subscript(bounds: Range<Int>) -> ArraySlice<Element>

按照苹果官方文档的说法,这个 ArraySlice 和原数组共享内存,同时也共享索引,这样做的目的一方面是能够节省内存,另一方面能够提高大数组的操作效率。

因此我们上边的例子中 subArr 有效的索引应该为 1、2(共享父数组索引)。这点可以通过打印索引看出来:

less 复制代码
var arr = [1, 2, 3, 4]
var subArr = arr[1..<3]
// 打印索引
print(subArr.indices) // 打印 1..<3

得出这个结论后,我们再尝试获取 subArr 的第一个元素,应该直接取下标 1:

css 复制代码
var arr = [1, 2, 3, 4]
var subArr = arr[1..<3]
print(subArr[1]) // 打印:2

如何解决这个隐患

这种设计难以用正常思维理解,我查看了一些论坛中的讨论,关于这个设计也不太容易修改掉,因为在集合及其子序列之间共享索引是 Swift 集合算法设计的重要组成部分(说白了就是积重难返了)。

要避免这个坑也有一些方法,比如,我们可以在 ArraySlice 的扩展中写一个新的下标方法来做一层保护(防止崩溃):

swift 复制代码
extension ArraySlice {
    subscript(`safe` index: Index) -> Element? {
        if indices.contains(index) {
            return self[index]
        } else {
            return nil
        }
    }
}

此类方法在之前的文章防止数组越界中也提到过。

其实苹果官方有提到,要安全地引用切片的开始和结束索引,始终使用 startIndex 和 endIndex 属性而不是特定值 ,也就是说,索引的开始位置不一定为 0,而是 startIndex,因此也可以在扩展中增加另一个方法,兼容这种情况:

swift 复制代码
extension ArraySlice {
    subscript(safeOffset offset: Int) -> Element? {
        let index = startIndex + offset
        return indices.contains(index) ? self[index] : nil
    }
}

这样的话,外界就可以默认切片的索引是从 0 开始的了,使用的时候:

ini 复制代码
var arr = [1, 2, 3, 4]
var subArr = arr[1..<3]
if let first = subArr[safeOffset: 0] {
    print(first) // 打印:2
}

这里每天分享一个 iOS 的新知识,快来关注我吧

本文同步自微信公众号 "iOS新知",每天准时分享一个新知识,这里只是同步,想要及时学到就来关注我吧!

相关推荐
黑化旺仔10 小时前
【OC】Runtime:类元数据、加载初始化与消息派发
开发语言·macos·ios·objective-c
songgeb11 小时前
iOS App支持设置RTL语言的实践
ios·swift·ui kit
软泡芙12 小时前
【iOS】swift启动屏显示黑屏
ios
用户693717500138413 小时前
当你向领导申请 AI 编程工具预算时,如果领导追问“能提效多少,有量化数据吗”,你该怎么回答?
android·后端·ios
Layer13 小时前
一些脱离 Mac 依赖的 Agent 操作 iPhone 实现
ios·ai编程·测试
用户693717500138415 小时前
当我说 Claude 搭配 GLM-5.3-Flash 太好用了,同事却提醒我:你可能正在失去对代码的掌控
android·后端·ios
茶底世界之下15 小时前
一键裁方会丢掉什么?用 Awaking 给园林照片做两种画幅
ios
Marco_Marco13517 小时前
定位SDK接入实战:Android/iOS/鸿蒙三端怎么选、怎么接
android·ios·harmonyos
老李IT笔记1 天前
关掉设备上的 AI 扩展:两个键各管一层,配错了等于没配
人工智能·ios·智能手机
2501_915921431 天前
iOS应用耗电测试:使用Energy Log、Keymob和Instruments进行性能监控与能源消耗分析
android·ios·小程序·https·uni-app·iphone·webview