窺探Swift之數組安全索引與數組切片


  今天是元宵節,祝大家元宵節快樂!在Swift中的數組和字典中下標是非常常見的,數組可以通過索引下標進行元素的查詢,字典可以通過鍵下標來獲取相應的值。在使用數組時,一個常見的致命錯誤就是數組越界。如果在你的應用程序中數組越界了,那么對不起,如果由着程序的性子的話是會崩潰的。為了防止崩潰呢,我們會對集合做一些安全的處理。比如對數組進行擴展,從而對數組的索引進行安全檢查,保證數組的index在正常范圍內。在Objective-C中也是經常對數組,字典等做一些處理操作。

  今天的博客的主要內容是先對Objective-C中常用集合的安全擴展進行介紹,由此在窺探一下Swift語言中的處理。並且還會介紹Swift中自定義下標,說白了自定義下標其實就是通過下標的形式與特定的下標值來訪問一個對象。自定義下標在有些場合中是非常實用的。然后下方還會給出數組切片的概念與實用方式。廢話少說進入今天的主題。

一、安全的索引集合元素

  對一個集合索引進行安全檢查是很有必要的,也是經常實用的,最常見的就是對數組和字典索引的安全檢查,該部分內容就是類比這Objective-C中的數組索引的安全檢查來擴充Swift的數組,從而讓你的Swift數組也同樣具備對數組安全檢查的功能。

  1. Objective-C中NSArray對索引的安全擴展

  下方這段代碼是非常簡單的,它是對Objective-C中的NSArray做的擴展,該方法位於NSArray相關的延展中。在你的項目中,如果添加了此段延展代碼,那么你就可以通過objectAtIndexSafe:方法對數組進行安全的索引。有代碼不難看出在定義該函數參數時,我們將index聲明為NSUInteger,也就是正整數,這就排除了你對下標傳入一個負數。緊接着又對index的合法性進行驗證,如果index不在數組有效范圍內,那么就返回nil。當你查找的元素不存在時,你返回nil是不會造成程序崩潰的,因為nil的地址是0x0, 這和歸零若引用有些類似。

  當然下方只是NSArray安全擴展其中一個方法,還有許多擴展的安全方法,比如數組的增刪改查都可以進行相應的安全擴展,擴展的方式和思路與下方這段簡單代碼類似,再次就不花過多的篇幅對其進行介紹了。

1 - (id)objectAtIndexSafe:(NSUInteger)index {
2     if (index > self.count-1) {
3         return nil;
4     }
5     return [self objectAtIndex:index];
6 }

  

  2.Swift中對Array的安全擴展

  上面簡單的對Objective-C中的安全方法進行了簡單的介紹,就算是對Swift相關內容的引子吧,下方將會給出Swift語言中類似的方法。對Swift相關方法介紹時,我會盡量的詳細一些,因為畢竟本篇博客主要是關於Swift內容的。接下來將對上面Objective-C中NSArray數組索引安全驗證的方法使用Swift語言進行重新。當然重寫的內容也是非常容易理解的。

    (1)主要是對subscript方法進行重載,在重載的subscript方法中,對index的范圍通過三目運算符進行了安全檢查。如果index在0..<count這個半開區間內,那么就返回當前索引的值,如果不在該范圍內就返回nil, 下方就是對Array索引的安全檢查。

1 extension Array {
2     subscript (safe index: Int) -> Element? {
3         return (0..<count).contains(index) ? self[index] : nil
4     }
5 }

    

    (2)上面是對Swift中的Array進行了安全索引擴展,接下來就是簡單的使用了,下方的代碼段是對上面安全擴展函數的測試。首先創建了一個數組testArray, 然后創建了一個索引數組indexs, 然后遍歷indexs中的元素值,將其作為testArray的下標,對testArray進行檢索。當然檢索時,使用的是我們上面定義的safe方法,並且在indexs下標數組中存在非法的下標。在這種情況下,我們來驗證一下我們的安全方法。

    當然在數組遍歷中,我們使用了for-in循環取出indexs中的每個index, 然后使用guard語句取出testArray中的值。使用guard語句能很好的過濾掉因為非法的index而返回的nil值。具體代碼段如下所示:

    上面的代碼段理解起來並不難,上述測試代碼的運行結果如下所示,從運行結果可以很好的說明問題,並且在index非法時不會崩潰,並合理的給出相應的錯誤提示,請看下方具體運行結果。

    上面的延展也可以通過對整個集合類型,也就是CollectionType進行擴展,不過在擴展CollectionType時要對Index使用where子句進行限制,使Index必須符合Comparable協議,具體實現如下所示,不過下面的方法比較少用,因為一般是數組存在越界的情況,因為在字典中,如果你對一個不存在的鍵進行值的索引,會返回nil值,而不會崩潰。但是在數組中,你對不存在的index進行索引,就會拋出錯誤。下方是另一種處理方式,不過該方式用的比較少。

    實現下方延展后,同樣可以在數組中使用safe方法。

 

  

二、使用多個索引下標的數組

  延展的功能是非常強大的,該部分將會給出另一個數組的延展。該延展的功能是可以通過多個索引給數組設置值,以及通過多個索引一次性獲取多個數組的值。該功能是非常強大的,接下來將一步步實現該功能。

  1. 了解zip()函數以及Zip2Sequence

    在實現數組多個索引擴展時,需要使用到zip()函數,zip()函數接收兩個序列,並且返回一個Zip2Sequence類型的數據。zip()函數究竟是干嘛的呢?接下來將會通過一個小的實例來搞一下zip()函數。首先看一下Apple的幫助文檔上對zip()函數的介紹。具體如下所示:

     上面那句英文的意思大概就是“基於兩個基本序列構建了一個序列對,在序列對中,第i對,代表着每個基本序列中的第i個元素。”在zip函數定義的過程中,我們可以看到,zip()是一個泛型函數,其接收兩個SequenceType類型的參數,然后返回一個Zip2Sequence類型的數據。新創建的序列對就存在於Zip2Sequence中。說這么多還是來個小Demo實惠一些,通過一個小實例,看zip()函數的用法一目了然。

    (1) 創建兩個數組zip1和zip2, 將這兩個數組作為zip()函數的參數,將兩個數組進行合並。具體實現如下:

    (2) 通過上面的程序可以看出,zipSum是一個Zip2Sequence<Array<Int>, Array<Int>>類型的常量,我們可以使用dump()對zipSum常量進行打印,觀察其中的數據存儲結構,具體結構如下所示:

 

    輸出結果如下,由結果容易看出,在序列中有兩個元素,第一個元素對應着數組zip1, 第二個元素對應着數組zip2。 

 

    (3)接下來就是對zipSum這個序列通過for-in循環進行遍歷,下方就是對zipSum進行遍歷的代碼。

      上面對zipSum遍歷的結果如下所示,由下方輸出結果可知,輸出是成對遍歷的,如果某個數組中的元素是多余的,那么就會被忽略掉。

 

  2. 數組多個索引的延展實現

    在這個將要實現的延展中,我們對Array進行了擴展,在延展中對subscript方法進行重載,使其可以接受多個下標,並且對多個下標對應的值進行索引,並把索引結果組成數組。在subscript方法中通過get方法獲取索引相應的值,通過set方法為相應的索引值進行設置。下方代碼段就是該延展的實現:    

 1 extension Array {
 2     subscript(i1: Int, i2: Int, rest: Int...) -> [Element] {
 3         //通過實現get方法,獲取數組中相應的值
 4         get {
 5             var result: [Element] = [self[i1], self[i2]]
 6             for index in rest {
 7                 result.append(self[index])
 8             }
 9             return result
10         }
11         
12         //通過set方法,對數組相應的索引進行設置
13         set (values) {
14             for (index, value) in zip([i1, i2] + rest, values) {
15                 self[index] = value
16             }
17         }
18     }
19 }

 

    在上述延展的實現中,並沒有多少困難的地方。在subs兩個cript函數中,使用的是可變參數,subscript函數參數的個數是兩個以上(包括兩個)。然后就是通過zip()函數以及對zip()函數返回的結果集進行遍歷,從而對多個下標索引進行值的設置。經過上述延展,我們就可以通過多個索引對數組進行操作了。上述延展的使用方式如下: 

 

三、數組切片

  數組切片在OC中也是不存在的,是Swift新引入的概念,該部分將會對數組切片進行討論,研究一下數組切片的使用方式及其特點。下方先通過一個小Demo來看一下如何生成數組切片。下方代碼段先將一個字符串通過map函數轉換成一個數組arrayTest, 然后我們創建一個該數組的切片。下方代碼段創建了arrayTest數組中的下標3到下標6這個范圍區間中的切片,arraySlices就是數組切片變量,它是ArraySlice<String>類型的,具體代碼段如下所示。

 

  在數組切片中有一點需要注意,數組切片的下標與原始數組中的下標保持一致。如果要取出切片arraySlices中的第一個值,我們要使用arraySlices[3], 而不是arraySlices[0], 如果使用arraySlices[0]就會報錯,如下所示:

  

  因為數組是值類型,盡管切片與原數組有着對應的數組下標,但是切片是原始數組的部分拷貝,所以修改切片或者修改原數組,兩者互不影響,下方示例給出了該測試,如下所示:

   

  如果把切片轉換成枚舉,那么切片中與原始數組對應的下標關系將不存在,下方是將切片轉換成枚舉序列,然后對其進行遍歷,代碼如下:

  上述代碼段輸出結果如下:

    今天博客就先寫到這兒,關於數組的延展還有許多,以后有機會再討論。其實我們還可以通過一些方式來為我們自己的對象添加下標。也就是可以通過下標來訪問對象屬性,這個以后在討論吧。   


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM