标签:
原文:http://rypress.com/tutorials/objective-c/data-types/nsset
NSSet
, NSArray
, and NSDictionary
are the three core collection classes defined by the Foundation Framework. An NSSet
NSSet
, NSArray
, 和 NSDictionary是Foundation框架中三个主要集合类。一个NSSet对象是一个静态的无序的对象集合。Sets本身就是一个描述成员的最好单词,所以当回答诸如“这个对象是否是集合的成员的时候”,你可以使用set而不是数组去回答。
The basic collection classes of the Foundation Framework
集合可以存储OC的对象,对于基本C的数据类型来说必须将int之类的类型包装进NSnumber在存入set,array或者字典。
NSSet是不可变的,所以你不能在NSSet创建后增加或者删除元素。但是你可以对存储在其内部的对象进行修改。例如如果存储了一个叫做
NSMutableString对象,你可以对此对象发送setString等消息,这种现象也存在于NSMutableSet
和 NSCountedSet
.
一个NSSet可以通过setWithObjects:对象创建,他接受一个nil为结尾的对象列表。大多数例子都是使用NSString来解释,但是NSSet可以存储任何类型的OC对象,并且不需要为相同类型。
NSSet *americanMakes = [NSSet setWithObjects:@"Chrysler", @"Ford", @"General Motors", nil]; NSLog(@"%@", americanMakes);
NSSet也可以使用setWithArray: 这个方法可以将NSArray数组转化成NSSet。记住sets中存储的元素都是唯一的,所以也可以使用这个特性进行去重。例如:
NSArray *japaneseMakes = @[@"Honda", @"Mazda", @"Mitsubishi", @"Honda"]; NSSet *uniqueMakes = [NSSet setWithArray:japaneseMakes]; NSLog(@"%@", uniqueMakes); // Honda, Mazda, Mitsubishi
Sets对于存在其内部的袁术是强引用的。也就是说,一定要特别注意循环引用的发生,元素千万不要有对set本身的强引用。
快速枚举是对于set中元素进行迭代的首选方法,count方法计算set中所有元素个数。例如:
NSSet *models = [NSSet setWithObjects:@"Civic", @"Accord", @"Odyssey", @"Pilot", @"Fit", nil]; NSLog(@"The set has %li elements", [models count]); for (id item in models) { NSLog(@"%@", item); }
如果你对基于块的方式感兴趣,那么你可以使用enumerateObjectsUsingBlock:去对set进行枚举。这个方法只有一个参数就是^(id obj, BOOL *stop)。
obj
是当前 object, *stop指针可以设定当前迭代的退出,就像这样:
[models enumerateObjectsUsingBlock:^(id obj, BOOL *stop) { NSLog(@"Current item: %@", obj); if ([obj isEqualToString:@"Fit"]) { NSLog(@"I was looking for a Honda Fit, and I found it!"); *stop = YES; // Stop enumerating items } }];
上述代码中当迭代至@“Fit”元素的时候退出迭代,这就好比break语句一样。
注意既然set是武勋的,所以不能将其顺序作为外部依赖,NSSet不支持下标的方式进行访问元素。这也真是它和数组以及字段的一个主要区别。
处于与相等比较外,两个NSSet对象也可以其他比较,详见:
NSSet *japaneseMakes = [NSSet setWithObjects:@"Honda", @"Nissan", @"Mitsubishi", @"Toyota", nil]; NSSet *johnsFavoriteMakes = [NSSet setWithObjects:@"Honda", nil]; NSSet *marysFavoriteMakes = [NSSet setWithObjects:@"Toyota", @"Alfa Romeo", nil]; if ([johnsFavoriteMakes isEqualToSet:japaneseMakes]) { NSLog(@"John likes all the Japanese auto makers and no others"); } if ([johnsFavoriteMakes intersectsSet:japaneseMakes]) { // You‘ll see this message NSLog(@"John likes at least one Japanese auto maker"); } if ([johnsFavoriteMakes isSubsetOfSet:japaneseMakes]) { // And this one, too NSLog(@"All of the auto makers that John likes are Japanese"); } if ([marysFavoriteMakes isSubsetOfSet:japaneseMakes]) { NSLog(@"All of the auto makers that Mary likes are Japanese"); }
就像Foundation框架中其他集合一样,可以检查一个对象是否在一个set中。containsObject:方法返回BOOL来指示当前对象是否属于set。还有一个选择就是member:,当对象存在于set中的时候这个方法返回一个对象引用否则返回nil。
NSSet *selectedMakes = [NSSet setWithObjects:@"Maserati", @"Porsche", nil]; // BOOL checking if ([selectedMakes containsObject:@"Maserati"]) { NSLog(@"The user seems to like expensive cars"); } // nil checking NSString *result = [selectedMakes member:@"Maserati"]; if (result != nil) { NSLog(@"%@ is one of the selected makes", result); }
你可以使用objectsPassingTest:方法对set进行内容的过滤,这个方法接受一个块,这个块在每一个元素上都会被执行。这个块中返回YES的时候表示当前元素被包含进新的set中,如果是NO则排除该元素。下面的代码表示找出所有开头为C的元素。
NSSet *toyotaModels = [NSSet setWithObjects:@"Corolla", @"Sienna", @"Camry", @"Prius", @"Highlander", @"Sequoia", nil]; NSSet *cModels = [toyotaModels objectsPassingTest:^BOOL(id obj, BOOL *stop) { if ([obj hasPrefix:@"C"]) { return YES; } else { return NO; } }]; NSLog(@"%@", cModels); // Corolla, Camry
因为NSSet是不可变的,所以objectsPassingTest:方法将会创建新的NSSet对象去包含过滤后的元素。这是很多不可变类所共有的特点,但是就元素而言他们并没有被拷贝而是一种引用,也就是说要他们是指向相同的内存的。
Sets可以使用 setByAddingObjectsFromSet:方法进行组合。因为Set的元素具有唯一性,所以在组合过程中也将去重。
NSSet *affordableMakes = [NSSet setWithObjects:@"Ford", @"Honda", @"Nissan", @"Toyota", nil]; NSSet *fancyMakes = [NSSet setWithObjects:@"Ferrari", @"Maserati", @"Porsche", nil]; NSSet *allMakes = [affordableMakes setByAddingObjectsFromSet:fancyMakes]; NSLog(@"%@", allMakes);
可变Sets允许你动态地增加和删除元素,这可比不可变Set灵活多了。除了成员检查,在动态的增删效率上也要快于NSMutableArray。
可变Sets的创建方式和不可变Sets的创建方式雷同。或者你可以通过 setWithCapacity:
创建一个空的Set。参数是定义了初始化的容量,但是这个容量是会被自动扩展的。
NSMutableSet *brokenCars = [NSMutableSet setWithObjects: @"Honda Civic", @"Nissan Versa", nil]; NSMutableSet *repairedCars = [NSMutableSet setWithCapacity:5];
addObject:
和 removeObject:是
NSMutableSet提供的主要特色方法。注意当被增加对象已经在Set中存在的时候将什么也不做。
NSMutableSet *brokenCars = [NSMutableSet setWithObjects: @"Honda Civic", @"Nissan Versa", nil]; NSMutableSet *repairedCars = [NSMutableSet setWithCapacity:5]; // "Fix" the Honda Civic [brokenCars removeObject:@"Honda Civic"]; [repairedCars addObject:@"Honda Civic"]; NSLog(@"Broken cars: %@", brokenCars); // Nissan Versa NSLog(@"Repaired cars: %@", repairedCars); // Honda Civic
在可变Set中并没有objectsPassingTest:方法,但是你可以使用filterUsingPredicate:方法去实现相同的功能。Predicates的概念可能超出了本教程的范围,但是指向说这个东西足可以让查找和过滤变的很简单。幸运的是,NSPredicate类可以使用块进行初始化,从而我们不需要台关心它的格式化语法。
下面的代码片段是可变的基于predicate版本的Sets的过滤方法。再次强调,由于是可变数组,所以它都是在已有Set上进行操作的。
NSMutableSet *toyotaModels = [NSMutableSet setWithObjects:@"Corolla", @"Sienna", @"Camry", @"Prius", @"Highlander", @"Sequoia", nil]; NSPredicate *startsWithC = [NSPredicate predicateWithBlock: ^BOOL(id evaluatedObject, NSDictionary *bindings) { if ([evaluatedObject hasPrefix:@"C"]) { return YES; } else { return NO; } }]; [toyotaModels filterUsingPredicate:startsWithC]; NSLog(@"%@", toyotaModels); // Corolla, Camry
NSMutableSet也提供了一些基本的Set集合的操作方法。如下:
NSSet *japaneseMakes = [NSSet setWithObjects:@"Honda", @"Nissan", @"Mitsubishi", @"Toyota", nil]; NSSet *johnsFavoriteMakes = [NSSet setWithObjects:@"Honda", nil]; NSSet *marysFavoriteMakes = [NSSet setWithObjects:@"Toyota", @"Alfa Romeo", nil]; NSMutableSet *result = [NSMutableSet setWithCapacity:5]; // Union [result setSet:johnsFavoriteMakes]; [result unionSet:marysFavoriteMakes]; NSLog(@"Either John‘s or Mary‘s favorites: %@", result); // Intersection [result setSet:johnsFavoriteMakes]; [result intersectSet:japaneseMakes]; NSLog(@"John‘s favorite Japanese makes: %@", result); // Relative Complement [result setSet:japaneseMakes]; [result minusSet:johnsFavoriteMakes]; NSLog(@"Japanese makes that are not John‘s favorites: %@", result);
对可变Set的迭代和对不可变Set的迭代原理上都一言,但是这里有一个忠告,在迭代过程中千万不要去改变Set内容。
下面的例子中间的for in循环的迭代是错误的。因为它在迭代过程中删除了元素。
// DO NOT DO THIS. EVER. NSMutableSet *makes = [NSMutableSet setWithObjects:@"Ford", @"Honda", @"Nissan", @"Toyota", nil]; for (NSString *make in makes) { NSLog(@"%@", make); if ([make hasPrefix:@"T"]) { // Throws an NSGenericException: // "Collection was mutated while being enumerated" [makes removeObject:@"Toyota"]; } }
最查档的做法如下,通过一个临时的拷贝来过滤出目标元素,然后在远Set中删除对应的元素。
NSMutableSet *makes = [NSMutableSet setWithObjects:@"Ford", @"Honda", @"Nissan", @"Toyota", nil]; NSArray *snapshot = [makes allObjects]; for (NSString *make in snapshot) { NSLog(@"%@", make); if ([make hasPrefix:@"T"]) { [makes removeObject:@"Toyota"]; } } NSLog(@"%@", makes);
NSCountedSet类非常值得介绍一下。他是NSMutableSet的子类,它能够记录某一个元素被的个数。内部维护了一个高效的对象计数器。
NSCountedSet和mutable之间的主要差别是countForObject:方法。这个方法也可以替换containsObject:方法。例如:
NSCountedSet *inventory = [NSCountedSet setWithCapacity:5]; [inventory addObject:@"Honda Accord"]; [inventory addObject:@"Honda Accord"]; [inventory addObject:@"Nissan Altima"]; NSLog(@"There are %li Accords in stock and %li Altima", [inventory countForObject:@"Honda Accord"], // 2 [inventory countForObject:@"Nissan Altima"]); // 1
Please see the official documentation for more details.
标签:
原文地址:http://www.cnblogs.com/ios123/p/4976696.html