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
        }
    }
}

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

其实苹果官方有提到,要安全地引用切片的开始和结束索引,始终使用 startIndexendIndex 属性而不是特定值 ,也就是说,索引的开始位置不一定为 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新知",每天准时分享一个新知识,这里只是同步,想要及时学到就来关注我吧!

相关推荐
1024小神11 小时前
在swiftui中使用实现类似微信右上角的功能菜单
ios
北京自在科技12 小时前
iOS 18.4修复多个核心安全漏洞,间接增强Find My服务的数据保护能力
科技·ios·iphone·蓝牙·find my·北京自在科技
Swift社区12 小时前
Swift 解 LeetCode 250:搞懂同值子树,用递归写出权限系统检查器
开发语言·leetcode·swift
大熊猫侯佩13 小时前
“群芳争艳”:CoreData 4 种方法计算最大值的效率比较(上)
swift·排序·sort·array·coredata·nsarray·最大值 max
货拉拉技术15 小时前
iOS货运用户App组件路由器设计与实践
ios·架构
布多16 小时前
iOS分类中的关联对象:如何用运行时突破Category的存储限制
ios·面试·源码
东坡肘子18 小时前
切勿将辅助驾驶宣传成智能驾驶 | 肘子的 Swift 周报 #078
人工智能·swiftui·swift
鸿蒙布道师1 天前
鸿蒙NEXT开发数值工具类(TS)
android·ios·华为·harmonyos·arkts·鸿蒙系统·huawei
程序员老刘1 天前
谨慎升级macOS 15.4,规避 ITMS-90048 错误
flutter·macos·ios
90后的晨仔1 天前
iOS 蓝牙开发基础知识梳理
ios