简介
Collection是Foundation framework提供的用于存储和管理一组对象的类的集合。我们比较常用的有NSArray,NSDictionary和NSSet,这三者有共同点,也有不同点。
相同点
- 可以遍历存储于其中的对象
- 判断一个对象是不是存在于Collection中
- 获取Collection中的一个对象
- 可变性
大部分Collection中都只能存储对象(如果是int等非object类型,可以用NSNumber转化以后存入),且都有mutable和immutable两种形式,比如Array就分为NSArray和NSMutableArray,NSMutableArray是继承自NSArray的。如果一个Collection是mutable的,我们可以认为它比immutable的Collection多了以下两种能力:
- 给Collection添加一个object
- 从Collection中删除一个object
不可变的Collection最大的好处是线程安全,API绝对不能向外暴露可变的Collection。
不同点
虽然他们有很多共同点,但是也有很多不同点,了解这些不同点,有助于我们根据不同的情况选用适合的Collection,从而达到提高效率的目的,下面我们根据官方文档简单介绍一下他们的特点(Collections Programming Topics):
- Arrays(如NSArray和NSMutableArray): 有序的,可以通过索引获取到索引相对的内容
1、用一个已有的array构造一个新的array,可以用下面这个方法创建:
-(instancetype)initWithArray:(NSArray<ObjectType> *)array copyItems:(BOOL)flag;
如果flag传NO,表示array中的object只是retain一份,并没有被copy;如果flag传YES,表示array中的object都被copy了新的一份。更多关于copy的内容见下面章节copying collections。
2、获取一个object的索引(可以用于判断object是否存在于Array中),可以采用以下两个方法:
-(NSUInteger)indexOfObject:(ObjectType)anObject;
-(NSUInteger)indexOfObjectIdenticalTo:(ObjectType)anObject;
第一个方法只要值一致就可以了,第二个方法比较指针一致。
3、排序
- sorting with sort descriptions
- sorting with blocks
- sorting with functions and selectors
- Dictionaries(如NSDictionary和NSMutableDictionary):无序的,通过key-value的形式获取存储的内容,提供快速的insertion和deletion操作。
1、与Array相似,用构造函数 initWithDictionary:copyItems:新建一个Dictionary,在copyItems传NO的情况下,Dictionary中value的object没有做深拷贝。
2、key可以是任何一个实现了NSCopying协议,并且实现了 hash 和 isEqual:方法的object。由于Dictionary内部用一个hash table来管理存储在其内部的object,所以hash方法的效率直接影响查找value的效率。一般用NSString对象来作为key可以满足大部分需求。
如果key没有实现NSCopying协议,直接跑出runtime error,没有实现hash和isEqual方法的话,无法查找存储在hash table中key对应的value,hash方法的实现应该尽量保证唯一性。NSObject默认的hash方法如下,默认的hash方法仅仅返回对象指针的地址,同样的isEqual也仅仅比较两个对象的指针地址是否相等。如果NSObject对于这两个方法的实现可以满足需求,就不需要重写。
- (NSUInteger)hash {
return (NSUInteger)self;
}
3、Sort
Dictionary提供了根据key的排序输出value的方法
- Sets(如NSSet,NSMutableSet和NSCountedSet):无序的,提供快速的insertion和deletion操作,还提供快速查找一个object是否存在于set中;其中,NSSet和NSMutableSet中的内容不可重复,NSCountedSet中的object可以重复添加,但是同一个object添加多次只有一个object实例,NSCountedSet对于其中的object维护一个计数器,当添加的次数等于去除的次数,改object的实例会从Set中被remove掉。简单地说,set管理的是一些不重复的无序的object的集合(NSCountedSet除外,可以重复)。
1、initWithSet:copyItems:新建一个Set,在copyItems传NO的情况下,Set中的Object没有做深拷贝
2、set中的每个Object必须实现NSObject协议中的hash和IsEqual方法。
3、set没有排序方法,可以通过allObjects方法把set转化为array。
4、set的优势在判断object在不在一个set中,mutable set提供了一系列。
unionSet:
adds all the objects from another set which are not already present.
intersectSet:
removes all the objects which are not in another set.
minusSet:
removes all the objects which are in another set.
Copying Collections
-
浅拷贝和深拷贝(如图2所示)
浅拷贝仅给object发送一个retain消息,拷贝指针;深拷贝给object发送一个copyWithZone:消息,因此必须让object实现NSCopying协议。如果没有实现copyWithZone,将会抛出一个runtime error,如图3所示
实现NSCopying协议仅仅实现了one-deep-level copy,如果你要实现true deep copy,例如你想要archive和unarchive一个Collection,必须实现NSCoding协议,如果不实现NSCoding协议,直接archive,报如图4的错,遵循NSCoding协议的Object可以被序列化和反序列化
遍历(Enumeration)
- for循环
优点:可以获得Index,可以反向遍历;
缺点:需要创建中间对象
//Array
for (NSUInteger i = 0; i < Array.count; i++) {
id object = Array[i]
}
//Dictionary
NSArray *keys = [Dictionary allKeys];
for (NSUInteger i = 0; i < keys.count; i++) {
id key = keys[i];
id object = [Dictionary objectForKey:key];
}
//Set
NSArray *tranArray = [Set allObjects];
Then same with array
- Fast Enumeration
优点:语法简洁,无中间变量,可以反向遍历[array reverseObjectEnumerator]
缺点:无法获得index
//Array
for(id object in self.testArray){
}
//Dictionay
for(id key in self.testDic){
id object = [self.testDic objectForKey:key];
}
//Set
for(id object in self.testSet) {
}
<NSFastEnumeration>协议只提供一个方法,如果需要一个类的对象实现快速遍历,就通过实现该协议来实现。
- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id __unsafe_unretained [])buffer count:(NSUInteger)len;
- Block Enumeration(推荐)
优点:可以获取index,不用生成中间变量,方便实现反向遍历,对于比较耗时的任务实现并发遍历比较方便,提供停止遍历的变量。
缺点:基本没有。
//Array
[Array enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
}];
//Dictionary
[Dictionary enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
}];
//Set
[Set enumerateObjectsUsingBlock:^(id _Nonnull obj, BOOL * _Nonnull stop) {
}];
//另一个block遍历方法,以Array为例,Dictionary和Set分别也实现了对应的方法。
//NSEnumerationOptions是一个枚举类型,
//NSEnumerationConcurrent实现每次遍历的块可以并发执行,对于耗时且顺序不重要的遍历可以使用并发遍历;NSEnumerationReverse实现反向遍历。
[Array enumerateObjectsWithOptions:(NSEnumerationOptions) usingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
}];
typedef NS_OPTIONS(NSUInteger, NSEnumerationOptions) {
NSEnumerationConcurrent = (1UL << 0),
NSEnumerationReverse = (1UL << 1),
};
- NSEnumerator(不建议)
NSEnumerator是一个抽象类,仅仅定义了两个方法供子类来实现,array,set,dictionary提供了special NSEnumerator objects来遍历自身的objects。这两个方法是:
- (nullable ObjectType)nextObject;
@property (readonly, copy) NSArray<ObjectType> *allObjects;
nextObject返回Collection中的下一个object,如果没有下一个object,返回nil。