新增到 iOS 集合內的物件必須是物件。
作為集合,需要滿足三個基本的特性:獲取元素、查詢元素、遍歷元素;
如果集合是可變的還需要另外支援 新增元素 和 刪除元素。
Array
新增到 Array
內的元素都是有序的,同一物件可多次被新增到集合。和其它集合相比,Array
遍歷內部元素十分方便。
Array
內元素必須是物件 (NSPointArray
除外)。
Array 的分類
-
NSArray 初始化後元素就不可再增刪。
-
NSMuatbleArray 初始化後可以隨時新增或刪除元素物件。
-
NSPointArray 與
NSMuatbleArray
相似,區別是可以指定對元素的強/弱引用。也就是說NSPointArray
內部元素為物件 或nil
。
關於陣列的一些知識點:
indexOfObject
與indexOfObjectIdenticalTo
的區別?
兩個方法都是來判斷某一物件是否是 Array
集合內元素,如果是則返回該物件在集合內的索引。兩個方法的區別就在於兩個 API 判定的依據:indexOfObject
會使用 isEqualTo
方法將 Object
與集合元素進行比較。而 indexOfObjectIdenticalTo
則會比較 Object
與集合元素的指標是否相同。
- 關於陣列排序,單一條件的值比較排序使用
SortedArrayUsingComparator
或SortedArrayUsingSelector
很容易,如果給你兩個甚至多個排序條件呢?比如對學生分數排序,排序條件為,優先按總分降序排列,總分相同再比較數學成績降序排列,數學成績也相同比較語文成績降序排列··· ··· 前面的兩個排序方法當然也能滿足我們的需求,但是現在要討論的是另一種更優雅的實現:使用NSSortDescriptor
。
Student *p1 = [[Student alloc] initWithName:@"zhonger" id:@"1" score:552 math:130 chinese:90 english:120];
Student *p2 = [[Student alloc] initWithName:@"zhonger" id:@"1" score:552 math:130 chinese:90 english:120];
Student *p3 = [[Student alloc] initWithName:@"zhonger" id:@"1" score:552 math:130 chinese:90 english:120];
Student *p4 = [[Student alloc] initWithName:@"zhonger" id:@"1" score:552 math:130 chinese:90 english:120];
Student *p5 = [[Student alloc] initWithName:@"zhonger" id:@"1" score:552 math:130 chinese:90 english:120];
NSArray *students = @[p1, p2, p3, p4, p5];
NSSortDescriptor *des1 = [[NSSortDescriptor alloc]initWithKey:@"score" ascending:NO];
NSSortDescriptor *des2 = [[NSSortDescriptor alloc] initWithKey:@"math" ascending:NO];
NSSortDescriptor *des3 = [[NSSortDescriptor alloc] initWithKey:@"chinese" ascending:NO];
NSArray *newArray1 = [students sortedArrayUsingDescriptors:@[des1,des2,des3]];
NSLog(@"%@",newArray1);
複製程式碼
是不是比之前的排序方法可讀性提升了很多,而且程式碼後期也更好維護。
Dictionary
Dictionary
內部元素以 key:value
鍵值對的形式存在,新增到 Dictionary
內的鍵值對是無序的,並且具有同一 key
值的鍵值對物件重複新增會相互覆蓋。當某個鍵值對新增到 Dictionary
時,Dictionary
會對 key
進行深拷貝,而對 value
進行淺拷貝,已經新增到集合內部的鍵值對,key 值將不可更改。而 value 值為強引用,當 value
為可變物件時,修改 value
物件,字典內的 value
也會被修改。
key
為任何遵循 NSCopying
協議並實現了 hash
和 isEqual
方法的物件。
value
為任何物件(包括集合)
key
需要很好地實現雜湊方法,否則字典的設值、查詢、獲取、增加等操作耗時將線性增加。一般我們都用 NSString
作為 key
值,是因為 NSString
很好地實現了雜湊方法。這些操作的耗時為常量。
字典可以根據 value
值對 key
進行排序,排序後得到的是一個 key
陣列。
Dictionary 的分類
- NSDictionary 初始化後就不可再增刪鍵值對。
- NSMutableDictionary 初始化後可以隨時新增或刪除鍵值對。
- NSMapTable 類似
NSMutableDictionary
可以指定對value
的強/弱引用。也就是說NSMapTable
內部元素的value
值可以為nil
。
關於字典的一些知識點:
我們在使用 Dictionnary
時,一般將 String
作為 key
值 ,從而可以滿足絕大部分需求。但有時也會遇到使用自定義物件作為 key 的情況。那麼作為 key
的物件都需要滿足哪些條件呢?
key
必須遵循NSCopying
協議,因為當元素漸入字典後,會對key
進行深拷貝。key
必須實現hash
與isEqual
方法。(便於快速獲取元素並保證key
的唯一性)。
並不建議使用特別大的物件作為
key
,可能會導致效能問題。
Set
新增到 Set 內的元素都是無序的,同一物件只能被新增一次。和其它集合相比,Set 查詢內部元素十分快速。
set
集合元素要很好地實現hash
方法(時間複雜度為O(1)),否則時間複雜度為 O(k)
Set 的分類
- NSSet 初始化後就不可再增刪元素。
- NSMuatbleSet 初始化後可隨時增刪元素。
- NSCountedSet 每個元素都帶有一個計數,新增元素,計數為一。重複新增某個元素則計數加一;移除元素計數減一,計數為零則移除
- NSHashTable 和 NSMutableSet 類似,區別是可指定對元素的強/弱引用,內部元素可以為
nil
。
Index set
IndexSet
儲存的 Array
子集,儲存元素為 Array
的 index
索引,儲存形式為索引範圍。
eg: (0,3,4)->(NSRange(0,1),NSRange(3,2))
Index set 的分類
- NSIndexSet 初始化後就不可再增刪元素。
- NSMutableIndexSet 初始化後可隨時增刪元素。
使用場景一般為記錄陣列的某些元素,比如列表多選行,可以使用 NSIndexSet
來記錄選擇了哪行,而不是另外建立一個可變陣列
Index path
儲存巢狀陣列的某個元素 path
如(0,2,0)
- **NSIndexPath **
使用場景一般是 TableView
的 dataSource
代理方法。
嚴格意義上說,
indexPath
並不是集合。其只是記錄巢狀陣列內某子元素的路徑位置。此處遵從官方文件將indexPath
和集合一起討論。
深拷貝與淺拷貝
以下為官方定義,考慮到翻譯後理解可能會出現偏差,筆者並未進行翻譯,牆裂建議讀者自己閱讀加深理解。
Shallow Copies
There are a number of ways to make a shallow copy of a collection. When you create a shallow copy, the objects in the original collection are sent a retain
message and the pointers are copied to the new collection. Listing 1 shows some of the ways to create a new collection using a shallow copy.
Listing 1 Making a shallow copy
NSArray *shallowCopyArray = [someArray copyWithZone:nil];
NSDictionary *shallowCopyDict = [[NSDictionary alloc] initWithDictionary:someDictionary copyItems:NO];
複製程式碼
These techniques are not restricted to the collections shown. For example, you can copy a set with the copyWithZone:
method—or the mutableCopyWithZone:
method—or an array with initWithArray:copyItems:
method.
Deep Copies
There are two ways to make deep copies of a collection. You can use the collection’s equivalent of initWithArray:copyItems:
with YES
as the second parameter. If you create a deep copy of a collection in this way, each object in the collection is sent a copyWithZone:
message. If the objects in the collection have adopted the NSCopying
protocol, the objects are deeply copied to the new collection, which is then the sole owner of the copied objects. If the objects do not adopt the NSCopying
protocol, attempting to copy them in such a way results in a runtime error. However, copyWithZone:
produces a shallow copy. This kind of copy is only capable of producing a one-level-deep copy. If you only need a one-level-deep copy, you can explicitly call for one as in Listing 2.
Listing 2 Making a deep copy
This technique applies to the other collections as well. Use the collection’s equivalent of initWithArray:copyItems:
with YES
as the second parameter.
If you need a true deep copy, such as when you have an array of arrays, you can archive and then unarchive the collection, provided the contents all conform to the NSCoding
protocol. An example of this technique is shown in Listing 3.
Listing 3 A true deep copy
NSArray* trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData: [NSKeyedArchiver archivedDataWithRootObject:oldArray]];
複製程式碼