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新知",每天准时分享一个新知识,这里只是同步,想要及时学到就来关注我吧!

相关推荐
Frank学习路上9 小时前
【IOS】XCode创建firstapp并运行(成为IOS开发者)
开发语言·学习·ios·cocoa·xcode
程序员老刘13 小时前
Android 16开发者全解读
android·flutter·客户端
瓜子三百克15 小时前
CALayer的异步处理
macos·ios·cocoa
吴Wu涛涛涛涛涛Tao16 小时前
一步到位:用 Very Good CLI × Bloc × go_router 打好 Flutter 工程地基
flutter·ios
杂雾无尘18 小时前
开发者必看:如何在 iOS 应用中完美实现动态自定义字体!
ios·swift·apple
某非著名程序员20 小时前
Flutter 新手绕不过的坑:ListView 为啥顶部老有空白?
flutter·客户端
kymjs张涛20 小时前
零一开源|前沿技术周报 #6
前端·ios·harmonyos
与火星的孩子对话1 天前
Unity进阶课程【六】Android、ios、Pad 终端设备打包局域网IP调试、USB调试、性能检测、控制台打印日志等、C#
android·unity·ios·c#·ip
恋猫de小郭2 天前
Flutter Widget Preview 功能已合并到 master,提前在体验毛坯的预览支持
android·flutter·ios
万少3 天前
第五款 HarmonyOS 上架作品 奇趣故事匣 来了
前端·harmonyos·客户端