1.25. Allocating and Making Use of Sets

Problem

You would like to store an array of objects but you don’t want any one object to appear more than once in the array.

Solution

Use sets instead of arrays.

Discussion

Sets are very similar to arrays. The big difference is that sets allow objects to be added only once. The second time you try to add the same object, it will be rejected by the set. We use NSSet for immutable and NSMutableSet for mutable sets. Let’s have a look at an example of an immutable set:

NSString *hisName = @"Robert";
NSString *hisLastName = @"Kiyosaki";

NSString *herName = @"Kim";
NSString *herLastName = @"Kiyosaki";

NSSet *setOfNames = [[NSSet alloc] initWithObjects:
                     hisName,
                     hisLastName,
                     herName,
                     herLastName, nil];

NSLog(@"Set = %@", setOfNames);

We created an immutable set and passed 4 string objects to its initializer method. So let’s see what gets printed out to the console window with our NSLog:

Set = {(
    Kim,
    Robert,
    Kiyosaki
)}

You can see that the last name Kiyosaki was added only once to the list. Our set rejected the second addition of the same object to the list. It is very important to understand that a set doesn’t just do a comparison on where in memory an object sits, but it actually looks into its contents. hisLastName and herLastName are two separate variables, and they will sit in two different places in the memory. Our set, however, managed to understand that we are passing instances of NSString to it and did a comparison on the contents of these strings to find out that we had already added the Kiyosaki last name to the set. So only one instance ended up in the set.

Now let’s have a look at constructing mutable sets:

NSMutableSet *setOfNames = [[NSMutableSet alloc] initWithObjects:
                            hisName,
                            hisLastName, nil];

[setOfNames addObject:herName];
[setOfNames addObject:herLastName];

We simply used the addObject: method of NSMutableSet to add new objects to our set. You can also use the removeObject: method to remove an object. Again, remember that the contents of the object matter, not its memory address. So if you want to remove a string from the set, simply pass that string to the removeObject: method, even if your new string is in a different variable or somewhere else in memory. As long as the contents of that string/object are the same, you will get the results you want:

NSMutableSet *setOfNames = [[NSMutableSet alloc] initWithObjects:
                            hisName,
                            hisLastName,
                            herName,
                            herLastName, nil];

[setOfNames removeObject:@"Kiyosaki"];

NSLog(@"Set = %@", setOfNames);

And the results get printed to the console window:

Set = {(
    Kim,
    Robert
)}

If you want to fast enumerate all objects in a set, use the enumerateObjectsUsingBlock: method. The block object that you pass to this method should return no value and should have two parameters:

A key of type id

Contains the object in the set that is being currently enumerated.

A pointer to a boolean value of type BOOL

If you want to stop the enumeration at any time, simply place a boolean value of type YES into the memory address of this variable.

Let’s have a look at an example. Let’s say I want to try to find the string Kiyosaki in a set that I have:

[setOfNames enumerateObjectsUsingBlock:^(__strong id obj, BOOL *stop) {

  if ([obj isKindOfClass:[NSString class]]){
    NSString *string = (NSString *)obj;
    if ([string isEqualToString:@"Kiyosaki"]){
      NSLog(@"Found %@ in the set", string);
      *stop = YES;
    }
  }

}];

If the enumeration can find a string with the value of Kiyosaki in the set, we print a string to the console and terminate the enumeration by placing the value of YES into the second parameter of our enumerator block object.

There are other handy methods for sets. Use the count method to get the number of objects currently in a set. You can also use the allObjects method to get an array of all the objects in the set. If you want to extract an object from the set, with no concern for which one, call the anyObject on your set. This method will return, as its name implies, a random object in the set, no matter where in the set it is. You will get nil from this method if the set is empty.

Get iOS 5 Programming Cookbook now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.