Collections Programming Topics

Introduction

About Collections

在Cocoa和Cocoa Touch,collection是Foundation framework类,用来存储和管理对象组。它的主要角色是存储对象在数组,字典或者集合中。
这里写图片描述
这些类使得管理对象组的任务变得轻松。Foundation collections在OSX和iOS中很有效率,使用广泛。

At a Glance

Collections有许多特征相同。大多数的collections持有对象,有可变和不可变的变种。
所有的collection共享许多相同的任务,包括

  • 枚举collection中的对象
  • 决定某个对象是否在collection中
  • 访问collection中的各个对象呢
    可变collection允许其他的任务:
  • 添加对象到collection
  • 从collection中移除对象

虽然collections有很多相同的特征,也有很多重要的不同。你会发现一些collection比其他collection更适合某个特定任务。因为一个collection表现的怎么样取决于怎么使用它们,你应该挑选最适合某项任务的collection。

数组:有序集合

数组是任意对象的有序集合。举个例子,下图中数组的对象可以是任意猫狗对象的结合,如果这个数组可变,你可以添加更多的对象。
这里写图片描述

数组基础

一个NSArray对象管理不可变数组-即,当你创建这个数组后,你不能添加,移除或者代替对象。然而,你可以修改各个元素(如果它们支持修改)。集合的可变性不影响集合内部对象的可变性。如果数组不怎么改变,或者要变就大变,你应该使用不可变数组。
一个NSMutableArray对象管理一个可变数组,它允许你添加和删除entries,需要分配存储。举个例子,给定一个NSMutableArray对象,包含一个dog对象,你可以添加另外的dog,cat或者其他对象。你也可以,改变狗的名字-一般来说,你在NSArray能做的在NSMutableArray对象中也能做。如果数组会慢慢改变或者非常大,你应该使用可变数组-大的集合花费更多时间。
你可以使用initWithArray:初始器或者arrayWithArray:便利构造器创建任意数组类型的实例。

NSMutableArray *myMutableArray = [NSMutableArray arrayWithArray:myArray];

一般来说,你通过发送array…消息到NSArray或者NSMutableArray类来实例化一个数组。array…消息返回一个数组,包含你所传进来的元素。当你添加一个元素到NSMutableArray对象的时候,这个对象没有被复制(除非你传递了YES到initWithArray:copyItems:)。而是这个对象的强引用添加到了这个数组中。
在NSArry中,两个主要方法count和objectAtIndex:提供了所有其他接口方法的基础:

  • count 返回了数组元素的数量
  • objectAtIndex:通过索引访问数组元素,索引值从0开始

可变数组

在NSMutableArray,下面列出的方法,提供了添加,代替,删除元素的能力
- addObject:
- insertObject:atIndex:
- removeLastObject
- removeObjectAtIndex:
- replaceObjectAtIndex:withObject:

NSMutableArray *array = [NSMutableArray array];
[array addObject:[NSColor blackColor]];
[array insertObject:[NSColor redColor] atIndex:0];
[array insertObject:[NSColor blueColor] atIndex:1];
[array addObject:[NSColor whiteColor]];
[array removeObjectsInRange:(NSMakeRange(1, 2))];
// array now contains redColor and whiteColor

使用Arrays

你可以使用objectAtIndex方法访问数组元素。

NSString *someString = [arrayOfStrings objectAtIndex:2];

你可以抽取数组的子集(subArrayWithRange:)或者连接NSString对象数组的元素到一个字符串(componentsJoinedByString:)。除此外,你可以使用isEqualToArray来比较两个数组,使用firstObjectCommonWithArray:方法。
有两个方法你可以用来决定对象是否在一个数组里面indexOfObject:和indexObjectIdenticalTo:。还有两个变种,indexOfObject:inRange和indexOfObjectIdenticalTo:inRange你可以用来在数组的某个范围搜索。这个indexOfObject方法通过发送isEqual:消息测试一样。indexOfObjectIdenticalToObject:方法使用指针比较来测试同等。下面阐释了这两者的不同

NSString *yes0 = @"yes";
NSString *yes1 = @"YES";
NSString *yes2 = [NSString stringWithFormat:@"%@", yes1];

NSArray *yesArray = [NSArray arrayWithObjects:yes0, yes1, yes2, nil];

NSUInteger index;

index = [yesArray indexOfObject:yes2];
// index is 1

index = [yesArray indexOfObjectIdenticalTo:yes2];
// index is 2

排序数组

你可能需要基于某些标准对数组排序。例如,你可能需要将用户产生的字符串以字母顺序排序,或者你可能想要将数字按照升降排序。下图显示了按照lastname然后firstname排序的数组。Cocoa提供了便捷的方法对数组内容进行排序,例如sort descriptors,blockes和selectors。
这里写图片描述

通过Sort Descriptors排序

Sort descriptors(NSSortDescriptor实例)提供了方便和抽象的方法来描述排序顺序。Sort Descriptors提供了一些有用的特色。你可以轻松地执行大多数排序操作。你也可以使用sort descriptors和Cocoa 绑定来排序聂荣,例如,tableview。
如果你使用sortedArrayUsingDescriptors:或者sortUsingDescriptors:,sort descriptors提供了方便的方法对对象集合进行排序,这个排序使用对象的许多特征。给定一个字典数组,你可以使用last name然后first name排序内容。

//First create the array of dictionaries
NSString *last = @"lastName";
NSString *first = @"firstName";

NSMutableArray *array = [NSMutableArray array];
NSArray *sortedArray;

NSDictionary *dict;
dict = [NSDictionary dictionaryWithObjectsAndKeys:
                     @"Jo", first, @"Smith", last, nil];
[array addObject:dict];

dict = [NSDictionary dictionaryWithObjectsAndKeys:
                     @"Joe", first, @"Smith", last, nil];
[array addObject:dict];

dict = [NSDictionary dictionaryWithObjectsAndKeys:
                     @"Joe", first, @"Smythe", last, nil];
[array addObject:dict];

dict = [NSDictionary dictionaryWithObjectsAndKeys:
                     @"Joanne", first, @"Smith", last, nil];
[array addObject:dict];

dict = [NSDictionary dictionaryWithObjectsAndKeys:
                     @"Robert", first, @"Jones", last, nil];
[array addObject:dict];

//Next we sort the contents of the array by last name then first name

// The results are likely to be shown to a user
// Note the use of the localizedCaseInsensitiveCompare: selector
NSSortDescriptor *lastDescriptor =
    [[NSSortDescriptor alloc] initWithKey:last
                               ascending:YES
                               selector:@selector(localizedCaseInsensitiveCompare:)];
NSSortDescriptor *firstDescriptor =
    [[NSSortDescriptor alloc] initWithKey:first
                               ascending:YES
                               selector:@selector(localizedCaseInsensitiveCompare:)];

NSArray *descriptors = [NSArray arrayWithObjects:lastDescriptor, firstDescriptor, nil];
sortedArray = [array sortedArrayUsingDescriptors:descriptors

改变排序顺序在概念上和编程上都很轻松。

NSSortDescriptor *lastDescriptor =
    [[NSSortDescriptor alloc] initWithKey:last
                               ascending:NO
                               selector:@selector(localizedCaseInsensitiveCompare:)];
NSSortDescriptor *firstDescriptor =
    [[NSSortDescriptor alloc] initWithKey:first
                               ascending:NO
                               selector:@selector(localizedCaseInsensitiveCompare:)];
NSArray *descriptors = [NSArray arrayWithObjects:firstDescriptor, lastDescriptor, nil];
sortedArray = [array sortedArrayUsingDescriptors:descriptors];

特别的,从用户输入生成sort descriptors非常直接。

使用Blocks排序

你可以使用blocks排序数组。NSArray的sortedArrayUsingComparator:方法排序数组到新的数组,使用block来比较独享。NSMutableArray的sortUsingComparator:方法在原地排序,使用block比较对象,下面阐释了使用block进行排序。

NSArray *sortedArray = [array sortedArrayUsingComparator: ^(id obj1, id obj2) {

     if ([obj1 integerValue] > [obj2 integerValue]) {
          return (NSComparisonResult)NSOrderedDescending;
     }

     if ([obj1 integerValue] < [obj2 integerValue]) {
          return (NSComparisonResult)NSOrderedAscending;
     }
     return (NSComparisonResult)NSOrderedSame;
}];

使用函数和Selectors排序

下面阐释了sortedArrayUsingSelector:,sortedArrayUsingFunction:context:和sortedArrayUsingFunction:context:hint:方法的使用。这些方法中最复杂的就是sortedArrayUsingFunction:context:hint:。当你有一个很大的数组(N entries),你一次排序,然后改变很少(P个添加和删除,P远远小于N)这个hinted排序是最有效率的。你可以重新使用你初次排序的工作,概念上就是对N个旧items和P新items做一个合并排序。

NSInteger alphabeticSort(id string1, id string2, void *reverse)
{
    if (*(BOOL *)reverse == YES) {
        return [string2 localizedCaseInsensitiveCompare:string1];
    }
    return [string1 localizedCaseInsensitiveCompare:string2];
}

NSMutableArray *anArray =
    [NSMutableArray arrayWithObjects:@"aa", @"ab", @"ac", @"ad", @"ae", @"af", @"ag",
        @"ah", @"ai", @"aj", @"ak", @"al", @"am", @"an", @"ao", @"ap", @"aq", @"ar", @"as", @"at",
        @"au", @"av", @"aw", @"ax", @"ay", @"az", @"ba", @"bb", @"bc", @"bd", @"bf", @"bg", @"bh",
        @"bi", @"bj", @"bk", @"bl", @"bm", @"bn", @"bo", @"bp", @"bq", @"br", @"bs", @"bt", @"bu",
        @"bv", @"bw", @"bx", @"by", @"bz", @"ca", @"cb", @"cc", @"cd", @"ce", @"cf", @"cg", @"ch",
        @"ci", @"cj", @"ck", @"cl", @"cm", @"cn", @"co", @"cp", @"cq", @"cr", @"cs", @"ct", @"cu",
        @"cv", @"cw", @"cx", @"cy", @"cz", nil];
// note: anArray is sorted
NSData *sortedArrayHint = [anArray sortedArrayHint];

[anArray insertObject:@"be" atIndex:5];

NSArray *sortedArray;

// sort using a selector
sortedArray =
        [anArray sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)];

// sort using a function
BOOL reverseSort = NO;
sortedArray =
        [anArray sortedArrayUsingFunction:alphabeticSort context:&reverseSort];

// sort with a hint
sortedArray =
        [anArray sortedArrayUsingFunction:alphabeticSort
                                  context:&reverseSort
                                     hint:sortedArrayHint];

过滤数组

NSArray和NSMutableArray类提供了方法来过滤数组内容。NSArray提供了filteredArrayUsingPredicate:,这个方法返回新的数组,包含匹配指定predicate的对象。NSMutableArray增加了filterUsingPredicate:,这个方法评价接收者的内容和指定的predicate,只剩下匹配的对象。

NSMutableArray *array =
    [NSMutableArray arrayWithObjects:@"Bill", @"Ben", @"Chris", @"Melissa", nil];

NSPredicate *bPredicate =
    [NSPredicate predicateWithFormat:@"SELF beginswith[c] 'b'"];
NSArray *beginWithB =
    [array filteredArrayUsingPredicate:bPredicate];
// beginWithB contains { @"Bill", @"Ben" }.

NSPredicate *sPredicate =
    [NSPredicate predicateWithFormat:@"SELF contains[c] 's'"];
[array filterUsingPredicate:sPredicate];
// array now contains { @"Chris", @"Melissa" }

你也可以使用NSIdexSet对象过滤数组。NSArray提供了objectsAtIndexes:方法,返回新的数组包含提供的索引集合中的索引所指向的对象。NSMutableArray添加了removeObjectsAtIndexes:方法,允许你在原地使用索引集合过滤数组。

Dictionaries:Keys和Values的集合

字典管理keys和values对。字典中一个key和value对称为一个entry。每个entry有一个代表key的对象,代表key的value的对象。在字典中,keys是唯一的-即一个字典中没有两个keys是一样的。一个key可以是任何对象,只要这个对象采纳了NSCopying协议,实现了hash和isEqual方法。
这里写图片描述

字典基础

一个NSDictionary对象管理一个不可变字典-即,当你创建这个字典后,你不能添加,删除或者代替keys和values。然而,你可以调整修改各个values(如果它们支持修改),但是keys不能被修改。集合的可变性不影响集合中对象的可变性。如果字典不怎么改变或者要变就整个变的时候,你应该使用不可变字典。
一个NSMutableDictionary对象管理可变字典,允许你添加或者删除entries,需要自动管理存储。如果字典会慢慢改变,或者很大,你应该使用可变字典-因为大的集合花费更多时间初始化。
你可以使用initWithDictionary:初始器或者便利构造器dictionaryWithDictionary:生成一种类型的字典实例。
一般来说,你实例化一个字典是通过使用dictionary…消息到NSDictionary或者NSMutableDictionary类中。dictionary…消息返回一个字典。添加到字典的对象不是复制的(除非你传递了YES到initWithDictionary:copyItems:),而是往字典添加一个强引用。
在内部,一个字典使用hash table来组织它的存储,当给定key时提供快速的值访问。字典定义的方法使你远离了和hash table,hash 函数打交道的复杂性。这些方法直接拿key,而不是它们的hash形式。

使用可变字典

当从一个可变字典移除一个entry时,记住字典对这个key和value对象的强引用被遗弃了。如果再也没有这些对象的强引用,它们就被释放了。
向可变字典添加对象相对很直接。使用setObject:forKey实例方法来添加一个key-value对,或者代替某个对象。

NSString *last = @"lastName";
NSString *first = @"firstName";

NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObjectsAndKeys:
        @"Jo", first, @"Smith", last, nil];
NSString *middle = @"middleInitial";

[dict setObject:@"M" forKey:middle];

你也可以使用addEntriesFromDictionary:实例方法从另一个字典添加entries。如果两个字典包含相同的key,守信者的前一个value object释放,新的对象取代。

NSString *last = @"lastName";
NSString *first = @"firstName";
NSString *suffix = @"suffix";
NSString *title = @"title";

NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObjectsAndKeys:
    @"Jo", first, @"Smith", last, nil];

NSDictionary *newDict = [NSDictionary dictionaryWithObjectsAndKeys:
    @"Jones", last, @"Hon.", title, @"J.D.", suffix, nil];

[dict addEntriesFromDictionary: newDict];

对字典排序

NSDictionary提供了keysSortedByValueSelector:返回一个数组

NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
    [NSNumber numberWithInt:63], @"Mathematics",
    [NSNumber numberWithInt:72], @"English",
    [NSNumber numberWithInt:55], @"History",
    [NSNumber numberWithInt:49], @"Geography",
    nil];

NSArray *sortedKeysArray =
    [dict keysSortedByValueUsingSelector:@selector(compare:)];
// sortedKeysArray contains: Geography, History, Mathematics, English

你也可以使用blocks轻松的排序字典的keys。NSDictionary的keysSortedByValueUsingComparator:方法允许你使用block来排序keys。

NSArray *blockSortedKeys = [dict keysSortedByValueUsingComparator: ^(id obj1, id obj2) {

     if ([obj1 integerValue] > [obj2 integerValue]) {
          return (NSComparisonResult)NSOrderedDescending;
     }

     if ([obj1 integerValue] < [obj2 integerValue]) {
          return (NSComparisonResult)NSOrderedAscending;
     }
     return (NSComparisonResult)NSOrderedSame;
}];

使用定制keys

在大多数情况下,Cocoa提供的对象例如NSString对象应该足够胜任keys。在某些场景,也许需要定制对象作为字典中的keys。当使用定制对象作为keys,要记住一些重要的东西。
keys必须符合NSCopying协议。添加entries到字典的方法-无论是初始化,还是在修改过程中-不会直接添加key对象到字典。取而代之的是,它们复制每个key,将它们的copy添加到字典。当复制到字典后,字典拥有的keys的复制不应该再被修改。
keys必须实现hash和isEqual方法,因为字典使用hash table来组织它的存储,迅速的访问包含的对象。除此之外,字典的性能重托于hash函数。使用坏的hash函数,掉下来的性能可能会很严重。

重要 因为字典复制keys,keys必须符合NSCopying协议。当选择什么对象作为keys,记住这点。虽然你可以使用任何采纳NSCopying的对象并且实现hash和isEqual方法,但是使用大对象,例如NSImage通常是很差的设计,因为这样做会导致性能变差。

使用映射表

NSMapTable默认配置下和NSMutableDictionary一样持有对象。他允许额外的存储选项,你能够用来为某些特定情形作出改变,例如你需要高级的存储管理选项,或者你想要持有某种特定指针类型。举个例子,下图的map table配置为对其的值对象持有弱引用。
这里写图片描述
当你想要对keyvalue对使用弱引用时,你可以使用一个NSMapTable对象。例如,假设你有一个全局映射表包含某些对象。因为全局对象永远不会被手机,除非它们是弱应用,否则它们不会被释放,Map tables配置为持有弱引用不拥有它的内容,如果map table中的对象咩有强引用,这些对象就被释放了。上图中,对象D和对象E会被释放,其余的对象继续存在。

Sets:没有顺序的对象集合

一个set是没有顺序的对象集合。当元素的顺序不重要,而测试元素是否在一个集合中的性能是重要的时候,你可以使用sets作为数组的替换物。
这里写图片描述

Set基础

一个NSSet对象管理不同对象的不可变集合-即当你生成这个set后,你不能添加,移除或者代替对象。然而,你可以修改各个对象(如果它们支持修改)。集合的可变性不影响集合中对象的可变性。如果set不怎么改变要变就整个变,你应该使用不可变set。
NSMutableSet,NSSet的子类,是可变的不同对象集合,允许添加和输出,需要自动分配存储。如果set会变化,或者非常大,你应该使用可变的set-初始化大集合花费更多的时间。
NSCountedSet,NSMutablSet的子类,是一个可变set,你可以添加一个对象多次。换句话说,set的元素不需要不同。一个计数set也以bag著名。整个set对于每个插入的不同对象保持一个计数器。NSCountedSet对象记录插入对象的数量,当移除对象时,要求移除计数器中数量。因此,在计数set中只有一个对象实例,即使这个对象添加了多次。countForObject方法返回对象添加到这个set的次数。
set中的对象必须相应NSObject协议方法hash和isEqual。如果set中存储了可变对象,对象的hash方法不能取决于可变对象的内部状态,可变对象当在set中时也不能修改。举个例子,可以将可变字典放入set中,但是当字典在set中时,你不能修改它。
NSSet提供了许多初始化方法,例如setWithObjects:和initWithArray:,返回一个NSSet对象。不复制添加到set中的对象(除非传递了YES到initWithSet:copyItems:)。而是将对对象的强引用添加到set中。
Sets,排除NSCountedSet,当你想要确保对象只出现一次,且添加同一对象多次没有效果时,是优先考虑的collection。

注意 如果set中的对象有良好的hash函数,访问一个元素,设置一个元素,移除一个元素都花费恒定时间。使用较差的hash函数,这些操作花费线性时间,例如NSString类有良好的hash函数。

可变集合

你可以使用NSSet提供的初始器创建一个新的NSMutableSet对象。你可以从NSSet实例创建一个NSMutableSet对象,使用setWithSet或者initWithSet方法
NSMutablSet类提供了向set添加对象的方法

  • addObject:添加单个对象到set
  • addObjectsFromArray:添加数组中的对象到set
  • unionSet:添加另一个set中的所有不存在于当前set
    NSMutableSet也提供了移除的方法
  • intersectSet:移除所有不存在于另一个set的对象
  • removeAllObjects 移除set中所有的对象
  • removeObject 移除set中特定的对象
  • minusSet 移除所有在另一个set的对象

因为NSCountedSet是NSMutableSet的子类,它继承了所有这些方法。然而,其中的某些方法在NSCountedSet中表现略有不同。举个例子

  • unionSet: 添加另一个set中所有的对象,即使它们已经存在于当前set
  • intersectSet:移除所有不在另一个set中的对象。如果两个set中同时有某个对象的多个实例,结果set包含这个对象,次数取最小的。
  • minusSet:移除所有在另一个set中的对象。如果一个对象存在多次,只是移除它的一个实例。

使用Sets

NSSet类提供了查询set元素的方法:
- allObjects 返回一个数组,包含了set中的所有对象
- anyObject 返回set中的某个对象
- count 返回set中对象的数量
- member:返回set中和这个指定对象一样的对象
- intersectsSet: 测试两个sets是否共享至少一个对象
- isEqualToSet:测试两个sets是否一样
- isSubsetOfSet:测试该set中的对象是否存在于另一个set中

NSSet方法objectEnumerator让你能够遍历这个set。makeObjectsPerformSelector:和makeObjectsPerformSelector:withObject:方法提供了向set中各个对象发送消息的方法。在大多数情况下,应该使用快速枚举因为它更快而且更灵活。

Hash Tables

NSHashTable类默认配置下就跟NSMutableSet一样持有对象。它也允许另外的存储选项,你可以用来为特定情境改变,例如,当你需要高级的存储管理选项,或者你想要持有具体类型的指针。例如,下面的映射table配置为持有对元素的weak引用。
这里写图片描述
当你想要一个使用弱引用的无序元素集合时,你可以使用NSHashTable。举个例子,假如你有一个全局hash table,包含了某些对象。因为全局对象永远不会被收集,除非它们是弱应用,否则不能被解分配。配置为持有弱应用的hash table不拥有它的内容。如果对hash
table中的对象没有强引用,这些对象就被解分配了。上图中的hash table对其内容持有弱引用。对象ACZ会被解分配,其余的对象会保持。
使用initWithOptions:capacity:方法和恰当的指针功能选项初始化一个hash table。你也可以使用initWithPointerFunctions:capacity:和NSPointerFunctions实例初始化。
NSHashTable类也定义了hashTableWithWeakObjects方法生成一个hash table,对其内容持有弱引用。只有当你存储对象时才应该使用。

Index Sets:存储索引到数组

你使用index sets存储索引到其它的数据结构,例如一个NSArray对象。在index set中的索引只能出现一次,这也是为什么index sets不适合存储任意整数的集合。因为index sets利用ranges来存储索引,它们比在数组中存储整数集合更加有效率。
这里写图片描述

Index Set基础

一个NSIndexSet对象管理不可改变的索引集合-即你创建这个index set以后,你不能添加索引或者移除索引。
NSMutableIndexSet对象管理可变的索引集合,允许添加和删除索引,需要自动分配存储。
你可以轻松的创建索引集合的实例,使用initWithIndexSet:。举个例子,如果你有一个NSMutableIndexSet对象名为myIndexes,你可以用下面的代码生成一个不可变的索引集合:

NSIndexSet *myImmutableIndexes=[[NSIndexSet alloc] initWithIndexSet: myIndexes];

你也可以从单个索引活着索引范围初始化一个索引集合,使用initWithIndex活着initWithIndexesInRange方法。

可变索引集

NSMutableIndexSet类的方法允许你添加或者移除索引或者索引范围。你可以,举个例子,存储不相交的索引集,根据需要修改已经存在的索引。下面列出了一些方法:

  • addIndex
  • addIndexsInRange
  • removeIndex
  • removeIndexsInRange

如果你有空的NSMutableIndexSet对象名为myDisjointIndexes,下面是一个填充索引的例子:

[myDisjointIndexes addIndexesInRange: NSMakeRange(1,2)];
[myDisjointIndexes addIndexesInRange: NSMakeRange(5,3)];
[myDisjointIndexes addIndex: 10];

迭代索引集

为了访问索引集合索引的所有对象,顺序迭代索引集可能是便捷的方法。迭代整个索引集合而不是对应的数组,更加有效率,因为它允许你检查你感兴趣的索引。如果你有一个NSArray对象名为anArray和一个NSIndexSet对象名为anIndexSet,你可以以下面的方式迭代索引集合。

NSUInteger index=[anIndexSet firstIndex];

while(index != NSNotFound)
{

     NSLog(@" %@",[anArray objectAtIndex:index]);
     index=[anIndexSet indexGreaterThanIndex: index];
}

有时候需要从后面开始迭代索引集合,例如,你想要选择性地从NSMutableArray对象中移除索引指向的对象。你可以通过下面的方式从后面开始迭代索引集合

NSUInteger index=[anIndexSet lastIndex];

while(index != NSNotFound)
{

     if([[aMutableArray objectAtIndex: index] isEqualToString:@"G"]){
          [aMutableArray removeObjectAtIndex:index];
     }
     index=[anIndexSet indexLessThanIndex: index];
}

上面的方法在你想要选择性地删除对象时有用,如果你想要删除所有索引集合中索引的对象,使用removeObjectsAtIndexes方法。

索引集合和block

索引集合和block一起使用的时候特别强大。blocks允许你生成索引集合,这些索引集合制定了数组中通过测试的成员。举个例子,如果你有未排序的数字数组,你想要生成一个索引集合,持有所有小于20的索引,你可以使用下面的代码。

NSIndexSet *lessThan20=[someArray indexesOfObjectsPassingTest:^(id obj, NSUInteger index, BOOL *stop){
     if ([obj isLessThan:[NSNumber numberWithInt:20]]){
          return YES;
     }
     return NO;
}];

索引集合也可以使用在数组中基于block的枚举。
另外,索引集合本身可以使用enumrateIndexesUsingBlock方法枚举。举个例子,你可以对索引在集合中的对象执行某些操作。

[anIndexSet enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop){
     if([[firstArray objectAtIndex: idx] isEqual:[secondArray objectAtIndex: idx]]){
          NSLog(@"Objects at %i Equal",idx);
     }
}];

索引路径:存储嵌套数组的路径

index paths存储通过嵌套数组的路径,用来获取更加复杂的集合层级中的对象,例如树。
这里写图片描述

索引路径基础

如果你考虑上图所示的层级,根数组只含有单个入口CEO。下面的数组包含不同的副主席。在每个副主席下面是主管数组。如果你想要将存储欧洲市场的某个雇员位置,例如,单个索引是不够的。使用索引路径访问嵌套数组非常必要。这个例子中,Bill T可以由0.0.1.1.2这个索引路径表示。
你可以从单个索引或者C风格的数组生成索引路径。下面的代码演示了如何创建到Bill T的索引路径。

NSUInteger arrayLength = 5;
NSUInteger integerArray[] = {0,0,1,1,2};
NSIndexPath *aPath = [[NSIndexPath alloc] initWithIndexes:integerArray length:arrayLength];

你也可以从许多其他的复杂层级集合类中自动的生成索引路径。举个例子NSTreeNode的indexPath方法。

使用索引路径

NSIndexPath提供了查询路径中的元素方法。举个例子,indexAtPosition:方法返回索引路径中制定位置的索引。你可以通过添加新索引或者移除最后的索引来创建新的索引路径。一些类使用索引路径管理他们的内容。NSTreeController就是这样一个例子。为了获得更多关于NSTreeController和索引路径的信息,查阅Cocoa Bindings Programming Topics。
在iOS中,UITableView和它的代理和数据源使用索引路径来管理内容,处理用户交互。UIKit添加了NSIndexPath的编程接口来将table的行和部分包含到索引路径中。

复制集合

对象复制有两种:浅复制和深复制。通常的复制是浅复制,生成一个集合和原来的集合共享对象。深复制从原先的集合中生成新的对象,然后添加到新的集合中。
这里写图片描述

浅复制

对集合做浅复制有很多方法。当你生成一个浅复制时,原先集合中的对象收到了retain消息,指针复制到了新的集合。下面演示了使用浅复制生成新集合的方法。

NSArray *shallowCopyArray = [someArray copyWithZone:nil];

NSDictionary *shallowCopyDict = [[NSDictionary alloc] initWithDictionary:someDictionary copyItems:NO];

这些技术不局限于集合。举个例子,你可以使用copyWithZone复制一个集合或者mutableCopyWithZone:方法或者initWithArray:copyItems:方法。

深复制

对集合做深复制有两种方法。你可以使用集合的initWithArray:copyItems:方法,传递YES。如果你使用这种方法创建了集合的深复制,集合中的每个对象受到copyWithZone消息。如果集合中的对象采纳了NSCopying协议,这些对象被复制到新的集合。如果这些对象不采纳NSCopying协议,用这种方法复制它们会导致运行时的错误。然而,copyWithZone:生成一个浅复制。这种类型的复制只能生成单层的复制。如果你需要单层的深复制,你可以使用下面的代码。

NSArray *deepCopyArray=[[NSArray alloc] initWithArray:someArray copyItems:YES];

如果你想要真正的深复制,例如当你有一个数组的数组,你可以archive和unarchive这个集合。

NSArray* trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:
          [NSKeyedArchiver archivedDataWithRootObject:oldArray]];

复制和可变

当你复制一个集合时,集合或者集合所包含的对象的可变性可能受到影响。

  • copyWithZone使得表面层不可变。所有深层的可变性保持不变
  • initWithArray:copyItems 第二个参数为No时,表面的可变性和分配的时候一样。所有深层次的可变性保持不变。
  • initWithArray:copyItems 第二个参数为YES时,表明的可变性和分配的时候一样。下一层是不可变的,所有更深的层的可变性保持不变。
  • Archiving和unarchiving集合不改变集合中各层的可变性。

枚举:便利集合的元素

Cocoa定义了三种主要的方法来遍历集合的内容。这些包括快速枚举和基于block的枚举。还有NSEnumerator类,尽管被快速枚举占尽风头。

快速枚举

枚举集合内容最好使用快速枚举,因为它提供下面的好处:
- 这个枚举比直接使用NSEnumerator类更加有效率
- 语法更简洁
- 当枚举的时候如果修改了集合会抛出异常
- 你可以同时进行多个枚举
集合类型不同,快速枚举表现也会略有不同。数组和sets枚举它们的内容,字典枚举他们的keys,NSIndexSet和NSIndexPath不支持快速枚举。

for (NSString *element in someArray) {
     NSLog(@"element: %@", element);
}

NSString *key;
for (key in someDictionary){
     NSLog(@"Key: %@, Value %@", key, [someDictionary objectForKey: key]);
}

对于更多关于快速枚举的信息,查阅The Objective-C Programming Language的快速枚举。

使用基于block的枚举

NSArray,NSDictionary和NSSet允许使用block来枚举他们的内容。

NSArray *anArray = [NSArray arrayWithObjects:@"A", @"B", @"D", @"M", nil];
NSString *string = @"c";

[anArray enumerateObjectsUsingBlock:^(id obj, NSUInteger index, BOOL *stop){
     if ([obj localizedCaseInsensitiveCompare:string] == NSOrderedSame) {
          NSLog(@"Object Found: %@ at index: %i",obj, index);
          *stop = YES;
     }
} ];
NSSet *aSet = [NSSet setWithObjects: @"X", @"Y", @"Z", @"Pi", nil];
NSString *aString = @"z";

[aSet enumerateObjectsUsingBlock:^(id obj, BOOL *stop){
     if ([obj localizedCaseInsensitiveCompare:aString]==NSOrderedSame) {
          NSLog(@"Object Found: %@", obj);
          *stop = YES;
     }
} ];

对于NSArray枚举,index参数对于并行枚举非常有用。没有这个参数,访问index的唯一方法就是使用indexOfObject方法,这很没有效率。stop参数对于性能来说非常重要,因为它能够允许枚举停止,当然是基于block中的某些条件。基于block的枚举方法在其他集合中名字和block签名略有不同。

使用枚举器

NSEnumerator是一个简单的抽象类,这个类的子类可枚举集合。集合对象-例如数组,sets和字典-提供了特殊的NSEnumerator对象,使用这些对象可以枚举它们的内容。你发送nextObject到NSEnumrator对象,让它返回集合中下一个对象。当集合耗尽时,返回nil。当它消耗完集合后,你不能重置枚举器。为了再次枚举集合,你必须生成新的枚举器。
集合类例如数组,set和字典包含了返回枚举器的方法。例如NSArray有两个方法返回NSEnumerator对象:objectEnumerator和reverseObjectEnumerator。NSDictionary类页游两个方法返回NSEnumerator对象:keyEnumerator和objectEnumerator。这些方法让你枚举NSDictionary对象的内容。
在OC中,一个NSEnumerator对象retains枚举的colletion
当通过枚举器枚举集合时,移除,代替或者添加可变集合的元素是不安全的。如果你需要在枚举的时候修改集合,你可以要么对集合做复制使用复制的对象枚举,要么在枚举的时候收集你需要的信息,枚举后再进行修改。

NSMutableDictionary *myMutableDictionary = <#Get a mutable dictionary#> ;
NSMutableArray *keysToDeleteArray =
    [NSMutableArray arrayWithCapacity:[myMutableDictionary count]];
NSString *aKey;
NSEnumerator *keyEnumerator = [myMutableDictionary keyEnumerator];
while (aKey = [keyEnumerator nextObject])
{
    if ( /* test criteria for key or value */ ) {
        [keysToDeleteArray addObject:aKey];
    }
}
[myMutableDictionary removeObjectsForKeys:keysToDeleteArray];
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值