iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 2
0
Mobile Development

iOS 開發筆記本系列 第 2

[Day 2] array[safe: index] vs array.safe[index]

# 起

Fatal error: Index out of range

不關菜鳥或是資深,在開發多過程中總是三不五時的遇到上面這個問題。

但是!在某些情況下,我們或許不在乎是否超出邊界,舉個例子:

假設我們畫面上固定顯示 10 筆資料,如果實際資料不足 10 筆,剩餘的部分就使用另外一種空值狀態,那麼我們可以針對 out of range (nil) 的狀況,來將畫面設置成空值的狀態。

# 承

承上,我們可以加上一些邊界的判斷:

extension Array {
    subscript(safe index: Int) -> Element? {
        if index < 0 || index > count - 1{
            return nil
        } else {
            return self[index]
        }
    }
}

let arr = [0, 1]
arr[safe: 2] // nill

或者使用 contains

extension Array {
    subscript(safe index: Int) -> Element? {
        if !indices.contains(index) {
            return nil
        } else {
            return self[index]
        }
    }
}

let arr = [0, 1]
arr[safe: 2] // nill

這在 stackoverflow 上有很多類似的做法。

看起來完美的解決的我們的問題,但是仔細看,會發現一點小小的問題是,safe: 表示的是什麼?它看起來像是針對 subscript parameter 的描述,但是沒有指明參數的作用是 Array index,如果今天使用這個 subscript 的人沒有看文件,沒有看到原始碼,是容易造成困惑的。

一個好的命名應該是不用看文件,也可以理解 API 的使用。

# 轉

話雖如此,但我還是使用了上面的寫法很長一段時間,因為我上面的 extension 是我寫的,所以並不會也困惑產生。直到某一天,我看到一篇文章(已經找不到出處了),針對 safe 這個命名空間,發表了一個我認為更好的寫法:

arr.safe[0]

這樣的寫法使用的 . 作為命名空間的間隔, .safe 看起來更像是,接下來的動作對於 array 而言是安全的操作,接下來用 Swift 對 Array index 的使用習慣: subscript with index 來做安全取值。

# 合

那麼我們要怎麼寫,才能達到上面的使用方法呢:

public struct SafeCollectionable<Base> where Base: Collection {
    
    let base: Base
    init(_ base: Base) {
        self.base = base
    }
    
    public subscript(_ index: Base.Index) -> Base.Element? {
        if !base.indices.contains(index) { 
            return nil 
        }
        return base[index]
    }
}

public extension Collection {
    var safe: SafeCollectionable<Self> {
        return SafeCollectionable(self)
    }
}

完成囉!宣告一個 SafeCollectionable,然後利用 extensionCollection 增加一個 safe: SafeCollectionablecomputed property 就可以達到我們想要的效果了!

#結

歡迎到我的網站逛逛。


上一篇
[Day 1] Xcode Tip
下一篇
[Day 3] 自動化的應用 part.1
系列文
iOS 開發筆記本5

1 則留言

0
ytyubox
iT邦新手 5 級 ‧ 2019-09-20 01:06:53

實用的文章!

分享 test case

  func testArraySafe() {
        let list = [1,2]
        var index = 3
        let output = list.safe[index]
        XCTAssertNil(output)
        index = 1
        XCTAssertEqual(list[index], list.safe[index])
    }
ohlulu iT邦新手 5 級‧ 2019-09-20 13:30:20 檢舉

感謝 text case ~

我要留言

立即登入留言