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

相关推荐
AaronMan19 小时前
iOS 中的 Subscription 与 RACDisposable:订阅生命周期管理
ios
代码的小搬运工21 小时前
【iOS】天气预报仿写总结
ios·objective-c·cocoa
代码的小搬运工21 小时前
【iOS】3G-Share仿写总结
macos·ios·cocoa
Lvan的前端笔记1 天前
SwiftUI:iOS 常用视图组件速查
ios·swiftui·swift
2501_916007471 天前
iOS和macOS应用程序性能分析和优化工具使用综合指南
android·macos·ios·小程序·uni-app·iphone·webview
智购科技无人售货机工厂1 天前
自动售货机硬件选型避坑:单片机 / 树莓派 / ESP32 怎么选?~YH
人工智能·单片机·嵌入式硬件·r语言·swift·perl·symfony
游戏开发爱好者81 天前
iOS开发IDE有哪些 Xcode 和 快蝎 轻量替代方案
ide·vscode·ios·个人开发·xcode·swift·敏捷流程
ZZH_AI项目交付2 天前
同一套前摄心率算法,为什么 iPhone 16 Pro 稳,iPhone XR 会失准?
ios·app·ai编程
DeMinds2 天前
内容没有丢,我为什么总在重新整理?|DeMinds 如何让工作接着继续
ios·github·markdown