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

相关推荐
Swift社区18 小时前
LeetCode 401 二进制手表 - Swift 题解
算法·leetcode·swift
humors22119 小时前
[原创]AI工具:读取手机系统文件工具
windows·ios·安卓·鸿蒙·文件·苹果·读取
humors22120 小时前
[原创]AI工具:手机文件查杀病毒工具
windows·ios·手机·安卓·鸿蒙·杀毒·苹果
2501_915918411 天前
iOS性能测试工具 Instruments、Keymob的使用方法 不局限 FPS
android·ios·小程序·https·uni-app·iphone·webview
左左右右左右摇晃2 天前
Tasker笔记
ios·iphone
恋猫de小郭2 天前
Android Studio Panda 3 发布,CMP 导致的 Gemini 输入问题
android·ide·flutter·ios·android studio
2501_915918412 天前
iOS 混淆流程 提升 IPA 分析难度 实现 IPA 深度加固
android·ios·小程序·https·uni-app·iphone·webview
Batac_蝠猫2 天前
值类型与引用类型:struct 与 class 的分工
swift
Digitally2 天前
如何将文件从 Mac / 苹果笔记本传输至 iPad
macos·ios·ipad
2501_915909062 天前
React Native 上架 App Store:项目运行与审核构建的流程
android·ios·小程序·https·uni-app·iphone·webview