Chapter    3

Working with Object Collections

This chapter will help you work with arrays and dictionaries using the Foundation framework with Objective-C.

The recipes in this chapter will show you how to:

  • Create arrays using NSArray and NSMutableArray
  • Add, remove, and insert objects into arrays
  • Search and sort arrays
  • Use different procedures to iterate through arrays
  • Save array contents to the file system
  • Create dictionaries using NSDictionary and NSMutableDictionary
  • Add and remove objects from dictionaries
  • Use different procedures to iterate through dictionaries
  • Save dictionary contents to the file system
  • Create sets using NSSet and NSMutableSet
  • Compare sets based on their object contents
  • Use different procedures to iterate through sets
  • Add and remove objects from sets

NOTE: There are three types of objects collections to work with in Objective-C: arrays, dictionaries, and sets. Your choice of collection will depend on the needs of your app.

Arrays organize objects in lists that are indexed by integers.

Dictionaries organize objects with keys; each object in a dictionary is associated with a string key that you can use later to retrieve the object.

Sets contain objects, but don’t assume that they will be in any order be indexed. Objects in sets also must be unique (no duplicates). Retrieving objects from sets is very fast because sets don’t have the overhead of an index so you will see this used in situations where performance is a consideration.

3.1 Creating an Array

Problem

Your app requires you to group objects together in a list.

Solution

Objective-C has two Foundation classes named NSArray and NSMutableArray that you can use to create lists of objects. Use NSArray when you have a list that you know you won’t need to change on the fly and NSMutableArray when you know you will need to add and remove objects from the array at a later time.

How It Works

Arrays are created in Objective-C like other objects: you use the alloc and init constructors or convenience functions like arrayWithObjects to create the array. If you use NSArray to create your array, you can’t make any changes to the array once the array is created. Use NSMutableArray to create arrays that you can later modify.

Here is an example of creating an array of strings:

NSArray *listOfLetters = [NSArray arrayWithObjects:@"A", @"B", @"C", nil];

When you use arrayWithObjects to create your array, you must pass in the objects in a comma-separated list that ends with nil. This example used NSString objects but you can use any object that you like with NSArray and NSMutableArray, including objects instantiated from your custom classes.

If you choose to use NSMutableArray, you can use the same constructors to create your arrays (NSMutableArray is a subclass of NSArray). You can also create your NSMutableArray by using alloc and init since you will likely add objects to your array at some future point. See Table 3-1 for a complete list of available constructors for NSArray and NSMutableArray and Listing 3-1 for the code.

Image

The Code

Listing 3-1. main.m

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[])
{

    @autoreleasepool {

        NSArray *listOfLetters1 = [NSArray arrayWithObjects:@"A", @"B", @"C", nil];

        NSLog(@"listOfLetters1 = %@", listOfLetters1);

        NSNumber *number1 = [NSNumber numberWithInt:1];
        NSNumber *number2 = [NSNumber numberWithInt:2];
        NSNumber *number3 = [NSNumber numberWithInt:3];

        NSMutableArray *listOfNumbers1 = [[NSMutableArray alloc]Image
 initWithObjects:number1, number2, number3, nil];

        NSLog(@"listOfNumbers1 = %@", listOfNumbers1);

        id list[3];
        list[0] = @"D";
        list[1] = @"E";
        list[2] = @"F";

        NSMutableArray *listOfLetters2 = [[NSMutableArray alloc] initWithObjects:listImage
 count:3];

        NSLog(@"listOfLetters2 = %@", listOfLetters2);

    }
    return 0;
}

Usage

To use this code, build and run your Mac app from Xcode. Inspect the log to see the contents of each array. In the next recipe, you will see how to reference each of these array elements so you can print out their contents to the log or use them elsewhere in your programs.

3.2 Referencing Objects in Arrays

Problem

You would like to get references to the objects in your arrays to either access their properties or to send messages to the objects.

Solution

Use the objectAtIndex: method to get a reference to an object in the array that corresponds to an integer position. You may also get a reference to the last object in an array using the lastObject function.

How It Works

NSArray organizes objects in a list that is indexed by integers starting with the number 0. If you want to get a reference to an object in your array and know the position of the object, you can use the objectAtIndex: function to get a reference to that object.

NSString *stringObject1 = [listOfLetters objectAtIndex:0];

There is also a very convenient function named lastObject that you can use to quickly get a reference to the last object in your array.

NSString *stringObject2 = [listOfLetters lastObject];

Often, you won’t know where in the array your object is located. If you already have a reference to the object in question, you can use the indexOfObject: function with the object reference as a parameter to find out where in the array the object is located.

NSUInteger position = [listOfLetters indexOfObject:@"B"];

See Listing 3-2 for the code.

The Code

Listing 3-2. main.m

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[])
{

    @autoreleasepool {

        NSMutableArray *listOfLetters = [NSMutableArray arrayWithObjects:@"A", @"B",Image
 @"C", nil];

        NSString *stringObject1 = [listOfLetters objectAtIndex:0];

        NSLog(@"stringObject1 = %@", stringObject1);

        NSString *stringObject2 = [listOfLetters lastObject];

        NSLog(@"stringObject2 = %@", stringObject2);

        NSUInteger position = [listOfLetters indexOfObject:@"B"];

        NSLog(@"position = %lu", position);


    }
    return 0;
}

Usage

To use this code, build and run your Mac app from Xcode. You can see that the objects were successfully referenced by the output written to the console.

stringObject1 = A
stringObject2 = C
position = 1

3.3 Obtaining the Array Count

Problem

Your app is working with the content in your arrays and you need to know how many elements are in the array in order to present your content appropriately.

Solution

NSArray objects have a count property that you can use to find out how many elements are in the array.

How It Works

To use the count property, you can use dot notation (listOfLetters.count) on any array object or you can send the count message ([listOfLetters count]) to find out how many elements are in the array. See Listing 3-3 for the code.

The Code

Listing 3-3. main.m

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[])
{

    @autoreleasepool {

        NSMutableArray *listOfLetters = [NSMutableArray arrayWithObjects:@"A", @"B",Image
 @"C", nil];

        NSLog(@"listOfLetters has %lu elements", listOfLetters.count);

    }
    return 0;
}

Usage

To use this code, build and run your Mac app from Xcode. The log message will present the number of elements.

listOfLetters has 3 elements

3.4 Iterating Through an Array

Problem

You have an array of objects and you would like to be able to send the same message or access the same property for every object in the array.

Solution

NSArray objects come with three built-in ways to iterate through a list of objects. Many people use a for-each loop to iterate through each element in the array. This structure gives you the ability to set up lines of code that will be applied to each element in the array.

You can also use a method named makeObjectsPerformSelector:withObject: where you can pass in the name of the method that you want each object to perform along with one parameter.

Finally, you now have the option of using blocks of code as a parameter that will be applied to each object in the array using the enumerateObjectsUsingBlock: method. This method gives you the same thing as a for-each loop, but you don’t need to write the code for the loop itself and you get a parameter that will help you keep track of the current element’s index.

How It Works

Since you need content to use as an example for this recipe, create three NSMutableString objects to put into a new array.

NSMutableString *string1 = [NSMutableString stringWithString:@"A"];
NSMutableString *string2 = [NSMutableString stringWithString:@"B"];
NSMutableString *string3 = [NSMutableString stringWithString:@"C"];

NSArray *listOfObjects = [NSArray arrayWithObjects:string1, string2, string3, nil];

To go through this array with a for-each loop, you need to set up the loop and provide a local variable that you can reference that will represent the current object within the array. You also need to add the code that will execute for each object in your list.

for(NSMutableString *s in listOfObjects){
    NSLog(@"This string in lowercase is %@", [s lowercaseString]);
}

In this for-each loop, you go through each mutable string in the array and write out a message to the log. The lowercaseString function is an NSString function that returns the string but in all lowercase letters.

You can send a message to each object in your array without using a for-each loop by sending the makeObjectsPerformSelector:withObject: message. You need to pass in the method by using the @selector keyword and the name of the method in parentheses. Pass in the parameter value after the property decoration withObject:.

[listOfObjects makeObjectsPerformSelector:@selector(appendString:)
                               withObject:@"-MORE"];

What you are doing here is sending the appendString message with the string parameter @”-MORE” to every mutable string in the array. So now each string has been changed to include the extra characters at the end.

WARNING: Make sure that the objects in your array are capable of responding to the messages that you are trying to send. If you attempt to send a message to an object in your array that the object can’t respond to, your program will crash and you will get an unrecognized selector sent to instance error message.

You can use blocks to define a block of code that you can apply to each object in the array. Blocks are a way to encapsulate code so that the code can be treated as an object and therefore passed to another object as a parameter. The NSArray method enumerateObjectsUsingBlock: gives you the ability to execute a block of code for each element in an array.

[listOfObjects enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop)
{
    NSLog(@"object(%lu)'s description is %@",idx, [obj description]);
}];

This essentially gives you the same thing as the for-each loop but you only need to use one line of code. Also, the block comes with built-in parameters for the object that you need to reference and an index to help you keep track of your position in the array. In this example, you are using blocks to write out a message to the log for each object in the list using the object’s description function and the index parameter. See Listing 3-4 for the code.

The Code

Listing 3-4. main.m

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[])
{

    @autoreleasepool {

        NSMutableString *string1 = [NSMutableString stringWithString:@"A"];
        NSMutableString *string2 = [NSMutableString stringWithString:@"B"];
        NSMutableString *string3 = [NSMutableString stringWithString:@"C"];

        NSArray *listOfObjects = [NSArray arrayWithObjects:string1, string2, string3,Image
 nil];

        for(NSMutableString *s in listOfObjects){
            NSLog(@"This string in lowercase is %@", [s lowercaseString]);
        }

        [listOfObjects makeObjectsPerformSelector:@selector(appendString:)
                                       withObject:@"-MORE"];

        [listOfObjects enumerateObjectsUsingBlock:^(id obj, NSUInteger idx,Image
 BOOL *stop) {
            NSLog(@"object(%lu)'s description is %@",idx, [obj description]);
        }];

    }

    return 0;
}

Usage

To use this code, build and run your Mac app from Xcode. The log message will present the results of each way of iterating through the array that was created at the beginning.

This string in lowercase is a
This string in lowercase is b
This string in lowercase is c
object(0)'s description is A-MORE
object(1)'s description is B-MORE
object(2)'s description is C-MORE

3.5 Sorting an Array

Problem

You are using arrays to group your custom objects and you would like the objects to appear in lists sorted by the values of the object’s properties.

Solution

Create one NSSortDescriptor object for each property that you want to use to sort your array. Put all these NSSortDescriptor objects into an array, which you will use as a parameter in the next step. Use the NSArray sortedArrayUsingDescriptors: method and pass the array of NSSortDescriptor objects as a parameter to return an array sorted by the properties that you specified.

How It Works

This recipe uses Person objects. See Listing 3-5 for the class definition of a Person object. While you can use this recipe to sort objects like strings or numbers, you can really see the power behind NSSortDescriptor when you use it with custom objects.

Your custom class is named Person and has three properties: firstName, lastName, and age. Your Person class also has two methods: reportState and initWithFirstName:lastName:andAge, which is a custom constructor.

First, create an array of Person objects.

//Instantiate Person objects and add them all to an array:
Person *p1 = [[Person alloc] initWithFirstName:@"Rebecca"
                                      lastName:@"Smith"
                                        andAge:33];
Person *p2 = [[Person alloc] initWithFirstName:@"Albert"
                                      lastName:@"Case"
                                        andAge:24];
Person *p3 = [[Person alloc] initWithFirstName:@"Anton"
                                      lastName:@"Belfey"
                                        andAge:45];
Person *p4 = [[Person alloc] initWithFirstName:@"Tom"
                                      lastName:@"Gun"
                                        andAge:17];
Person *p5 = [[Person alloc] initWithFirstName:@"Cindy"
                                      lastName:@"Lou"
                                        andAge:6];
Person *p6 = [[Person alloc] initWithFirstName:@"Yanno"
                                      lastName:@"Dirst"
                                        andAge:76];

NSArray *listOfObjects = [NSArray arrayWithObjects:p1, p2, p3, p4, p5, p6,  nil];

If you print out each element in this array, the objects will appear in the order that you put them into the array. If you want to sort this array by each person’s age, last name, and first name, you can use NSSortDescriptor objects. You need one sort descriptor for each Person property that you’re using to sort.

//Create three sort descriptors and add to an array:
NSSortDescriptor *sd1 = [NSSortDescriptor sortDescriptorWithKey:@"age"
                                                      ascending:YES];

NSSortDescriptor *sd2 = [NSSortDescriptor sortDescriptorWithKey:@"lastName"
                                                      ascending:YES];

NSSortDescriptor *sd3 = [NSSortDescriptor sortDescriptorWithKey:@"firstName"
                                                      ascending:YES];

NSArray *sdArray1 = [NSArray arrayWithObjects:sd1, sd2, sd3, nil];

You must pass in the name of the property as a string and specify whether you want that property sorted ascending or descending. Finally, all the sort descriptors need to be in an array.

The position of each sort descriptor in the array determines the order in which the objects will be sorted. So, if you want the array sorted by age and then last name, make sure you add the sort descriptor corresponding to age before the sort descriptor corresponding to last name.

To get your sorted array, send the sortedArrayUsingDescriptors message to the array that you want to sort and pass the array of sort descriptors as a parameter.

NSArray *sortedArray1 = [listOfObjects sortedArrayUsingDescriptors:sdArray1];

To see the results, use the makeObjectsPerformSelector method to have each object in the sorted array report its state to the log.

[sortedArray1 makeObjectsPerformSelector:@selector(reportState)];

This will print out the details of each Person object to the log in the order that was specified by the sort descriptors (age, last name, first name). See Listings 3-5 through 3-7 for the code.

The Code

Listing 3-5. Person.h

#import <Foundation/Foundation.h>

@interface Person : NSObject

@property(strong) NSString *firstName;
@property(strong) NSString *lastName;
@property(assign) int age;

-(id)initWithFirstName:(NSString *)fName lastName:(NSString *)lName andAge:(int)a;

-(void)reportState;

@end

Listing 3-6. Person.m

#import "Person.h"

@implementation Person

@synthesize firstName, lastName, age;

-(id)initWithFirstName:(NSString *)fName lastName:(NSString *)lName andAge:(int)a{
    self = [super init];
    if (self) {
        self.firstName = fName;
        self.lastName = lName;
        self.age = a;
    }
    return self;
}

-(void)reportState{
    NSLog(@"This person's name is %@ %@ who is %i years old", firstName, lastName, age);
}

@end

Listing 3-7. main.m

#import <Foundation/Foundation.h>
#import "Person.h"

int main (int argc, const char * argv[])
{

    @autoreleasepool {

        //Instantiate Person objects and add them all to an array:
        Person *p1 = [[Person alloc] initWithFirstName:@"Rebecca"
                                              lastName:@"Smith"
                                                andAge:33];

        Person *p2 = [[Person alloc] initWithFirstName:@"Albert"
                                              lastName:@"Case"
                                                andAge:24];

        Person *p3 = [[Person alloc] initWithFirstName:@"Anton"
                                              lastName:@"Belfey"
                                                andAge:45];

        Person *p4 = [[Person alloc] initWithFirstName:@"Tom"
                                              lastName:@"Gun"
                                                andAge:17];

        Person *p5 = [[Person alloc] initWithFirstName:@"Cindy"
                                              lastName:@"Lou"
                                                andAge:6];

        Person *p6 = [[Person alloc] initWithFirstName:@"Yanno"
                                              lastName:@"Dirst"
                                                andAge:76];

        NSArray *listOfObjects = [NSArray arrayWithObjects:p1, p2, p3, p4, p5, p6,Image
  nil];

        NSLog(@"PRINT OUT ARRAY UNSORTED");

        [listOfObjects makeObjectsPerformSelector:@selector(reportState)];

        //Create three sort descriptors and add to an array:
        NSSortDescriptor *sd1 = [NSSortDescriptor sortDescriptorWithKey:@"age"
                                                              ascending:YES];

        NSSortDescriptor *sd2 = [NSSortDescriptor sortDescriptorWithKey:@"lastName"
                                                              ascending:YES];


        NSSortDescriptor *sd3 = [NSSortDescriptor sortDescriptorWithKey:@"firstName"
                                                              ascending:YES];

        NSArray *sdArray1 = [NSArray arrayWithObjects:sd1, sd2, sd3, nil];

        NSLog(@"PRINT OUT SORTED ARRAY (AGE,LASTNAME,FIRSTNAME)");

        NSArray *sortedArray1 = [listOfObjects sortedArrayUsingDescriptors:sdArray1];

        [sortedArray1 makeObjectsPerformSelector:@selector(reportState)];

        NSArray *sdArray2 = [NSArray arrayWithObjects:sd2, sd1, sd3, nil];

        NSArray *sortedArray2 = [listOfObjects sortedArrayUsingDescriptors:sdArray2];

        NSLog(@"PRINT OUT SORTED ARRAY (LASTNAME,FIRSTNAME,AGE)");

        [sortedArray2 makeObjectsPerformSelector:@selector(reportState)];

    }
    return 0;
}

Usage

To use this code, you need to create a file for the Person class. This is an Objective-C class, and you may use the Xcode file templates to start it. The Person class must be imported into code located in main.m (for Mac command line apps). Build and run the project and then inspect the console log to see the results of the sorted arrays.

PRINT OUT ARRAY UNSORTED
This person's name is Rebecca Smith who is 33 years old
This person's name is Albert Case who is 24 years old
This person's name is Anton Belfey who is 45 years old
This person's name is Tom Gun who is 17 years old
This person's name is Cindy Lou who is 6 years old
This person's name is Yanno Dirst who is 76 years old
PRINT OUT SORTED ARRAY (AGE,LASTNAME,FIRSTNAME)
This person's name is Cindy Lou who is 6 years old
This person's name is Tom Gun who is 17 years old
This person's name is Albert Case who is 24 years old
This person's name is Rebecca Smith who is 33 years old
This person's name is Anton Belfey who is 45 years old
This person's name is Yanno Dirst who is 76 years old
PRINT OUT SORTED ARRAY (LASTNAME,FIRSTNAME,AGE)
This person's name is Anton Belfey who is 45 years old
This person's name is Albert Case who is 24 years old
This person's name is Yanno Dirst who is 76 years old
This person's name is Tom Gun who is 17 years old
This person's name is Cindy Lou who is 6 years old
This person's name is Rebecca Smith who is 33 years old

3.6 Querying an Array

Problem

You have a significant array full of objects and you would like to select a subset of this array based on some criteria to use in controls like the search bar in the table of an iOS app.

Solution

The first thing that you need is an NSPredicate object. NSPredicate is used to define a search query. Next, you can use the original array’s filteredArrayUsingPredicate function to return a subset of the original array based on the specifications of the NSPredicate: object that you defined.

How It Works

For this recipe, use the same Person objects as you did in Recipe 3.5. You are going to define a predicate to return an array containing only Person objects with an age greater than 30.

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"age > 30"];

Predicates require a logical expression to apply to the objects in the array. Here age is a property that must be part of the objects in the array that you are querying. You use the same comparison operators that you use in programming (see Table 3-2 for a complete list of NSPredicate operators).

Image

Image

Once you have your predicate set up, all you need to do is get the subset of the array by using the filteredArrayUsingPredicate: function while passing the NSPredicate object as a parameter.

NSArray *arraySubset = [listOfObjects filteredArrayUsingPredicate:predicate];

You will end up with another array that has only those objects that match the specifications that you coded in the NSPredicate object. See Listings 3-8 through 3-10 for the code.

The Code

Listing 3-8. Person.h

#import <Foundation/Foundation.h>

@interface Person : NSObject

@property(strong) NSString *firstName;
@property(strong) NSString *lastName;
@property(assign) int age;

-(id)initWithFirstName:(NSString *)fName lastName:(NSString *)lName andAge:(int)a;

-(void)reportState;

@end

Listing 3-9. Person.m

#import "Person.h"

@implementation Person

@synthesize firstName, lastName, age;

-(id)initWithFirstName:(NSString *)fName lastName:(NSString *)lName
andAge:(int)a{
    self = [super init];
    if (self) {
        self.firstName = fName;
        self.lastName = lName;
        self.age = a;
    }
    return self;
}

-(void)reportState{
    NSLog(@"This person's name is %@ %@ who is %i years old", firstName, lastName, age);
}

@end

Listing 3-10. main.m

#import <Foundation/Foundation.h>
#import "Person.h"

int main (int argc, const char * argv[])
{

    @autoreleasepool {
        //Instantiate Person objects and add them all to an array:
        Person *p1 = [[Person alloc] initWithFirstName:@"Rebecca"
                                              lastName:@"Smith"
                                                andAge:33];
        Person *p2 = [[Person alloc] initWithFirstName:@"Albert"
                                              lastName:@"Case"
                                                andAge:24];
        Person *p3 = [[Person alloc] initWithFirstName:@"Anton"
                                              lastName:@"Belfey"
                                                andAge:45];
        Person *p4 = [[Person alloc] initWithFirstName:@"Tom"
                                              lastName:@"Gun"
                                                andAge:17];
        Person *p5 = [[Person alloc] initWithFirstName:@"Cindy"
                                              lastName:@"Lou"
                                                andAge:6];
        Person *p6 = [[Person alloc] initWithFirstName:@"Yanno"
                                              lastName:@"Dirst"
                                                andAge:76];

        NSArray *listOfObjects = [NSArray arrayWithObjects:p1, p2, p3, p4, p5, p6,Image
  nil];

        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"age > 30"];

        NSArray *arraySubset = [listOfObjects filteredArrayUsingPredicate:predicate];

        NSLog(@"PRINT OUT ARRAY SUBSET");

        [arraySubset makeObjectsPerformSelector:@selector(reportState)];


    }
    return 0;
}

Usage

To use this code, build and run your Mac app from Xcode. Check the console to see the results of the query made by the NSPredicate object.

PRINT OUT ARRAY SUBSET
This person's name is Rebecca Smith who is 33 years old
This person's name is Anton Belfey who is 45 years old
This person's name is Yanno Dirst who is 76 years old

3.7 Manipulating Array Contents

Problem

You want your array content to be more dynamic so that you or your users can add, remove, and insert objects into arrays. However, NSArray is an immutable class, so once you create an NSArray you can’t make any changes to its contents.

Solution

If you know that your array needs to be dynamic, use NSMutableArray. NSMutableArray is a subclass of NSArray, so you can work with NSMutableArray as you would with NSArray. But NSMutableArray provides methods that let you add, remove, and insert objects into the array list.

How It Works

First, instantiate a NSMutableArray object. You can use any constructor to do this. To create a new empty NSMutableArray, you may simply use alloc and init.

NSMutableArray *listOfLetters = [[NSMutableArray alloc] init];

To add objects to this array, you must send the addObject: message to the array with the object that you are adding to the array as a parameter.

[listOfLetters addObject:@"A"];

[listOfLetters addObject:@"B"];

[listOfLetters addObject:@"C"];

When you use addObject: you are always adding objects to the end of the array list. If you would like to insert an object into another postion in the array, you need to use the insertObject:atIndex: method.

[listOfLetters insertObject:@"a"
                    atIndex:0];

This will insert the object into the first position in the array.

If you want to completely replace one object with another object at a particular index, you can use the replaceObjectAtIndex:withObject: method. Here is how to replace the string C with the lowercase c:

[listOfLetters replaceObjectAtIndex:2
                         withObject:@"c"];

To have two objects in the array exchange places, you can use the exchangeObjectAtIndex:withObjectAtIndex: method.

[listOfLetters exchangeObjectAtIndex:0
                   withObjectAtIndex:2];

When you need to remove objects from your array, you have a few different methods to choose from. You can remove an object at a specified index, you can remove the last object in the array, and you can remove all objects from the list. If you have a reference to the object on hand, you can also use that object reference to remove that object from the array. Here are some examples of removing objects:

[listOfLetters removeObject:@"A"];

[listOfLetters removeObjectAtIndex:1];

[listOfLetters removeLastObject];

[listOfLetters removeAllObjects];

See Listing 3-11 for the code.

The Code

Listing 3-11. main.m

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[])
{

    @autoreleasepool {

        NSMutableArray *listOfLetters = [[NSMutableArray alloc] init];

        [listOfLetters addObject:@"A"];

        [listOfLetters addObject:@"B"];

        [listOfLetters addObject:@"C"];

        NSLog(@"OBJECTS ADDED TO ARRAY: %@", listOfLetters);

        [listOfLetters insertObject:@"a"
                            atIndex:0];

        NSLog(@"OBJECT 'a' INSERTED INTO ARRAY: %@", listOfLetters);

        [listOfLetters replaceObjectAtIndex:2
                                 withObject:@"c"];

        NSLog(@"OBJECT 'c' REPLACED 'C' IN ARRAY: %@", listOfLetters);

        [listOfLetters exchangeObjectAtIndex:0
                           withObjectAtIndex:2];

        NSLog(@"OBJECT AT INDEX 1 EXCHANGED WITH OBJECT AT INDEX 2 IN ARRAY: %@", Image
 listOfLetters);

        [listOfLetters removeObject:@"A"];

         NSLog(@"OBJECT 'A' REMOVED IN ARRAY: %@", listOfLetters);

        [listOfLetters removeObjectAtIndex:1];

         NSLog(@"OBJECT AT INDEX 1 REMOVED IN ARRAY: %@", listOfLetters);

        [listOfLetters removeLastObject];

         NSLog(@"LAST OBJECT REMOVED IN ARRAY: %@", listOfLetters);

        [listOfLetters removeAllObjects];

         NSLog(@"ALL OBJECTS REMOVED IN ARRAY: %@", listOfLetters);

    }
    return 0;
}

Usage

To use this code, build and run your Mac app from Xcode. Check the console to see what happens to the array after each operation is applied.

OBJECTS ADDED TO ARRAY: (
    A,
    B,
    C
)
OBJECT 'a' INSERTED INTO ARRAY: (
    a,
    A,
    B,
    C
)
OBJECT 'c' REPLACED 'C' IN ARRAY: (
    a,
    A,
    B,
    c
)
OBJECT AT INDEX 1 EXCHANGED WITH OBJECT AT INDEX 2 IN ARRAY: (
    B,
    A,
    a,
    c
)
OBJECT 'A' REMOVED IN ARRAY: (
    B,
    a,
    c
)
OBJECT AT INDEX 1 REMOVED IN ARRAY: (
    B,
    c
)
LAST OBJECT REMOVED IN ARRAY: (
    B
)
ALL OBJECTS REMOVED IN ARRAY: (
)

3.8 Saving Arrays to the File System

Problem

You want to save the objects in your array to the file system to be used later or by another program.

Solution

If your array contains lists of number or string objects, you can save all of these to your file system to be used later. Use the writeToFile:atomically: method to do this. Note that this does not work with custom objects. Custom objects require you to adopt the NSCoding protocol and use an archiving class (Chapter 9) or Core Data (Chapter 10).

How It Works

For this recipe, create an array filled up with strings and numbers.

NSArray *listOfObjects = [NSArray arrayWithObjects:@"A", @"B", @"C", [NSNumber Image
  numberWithInt:1], [NSNumber numberWithInt:2], [NSNumber numberWithInt:3],  nil];

To save this to the file system, you first need a file reference.

NSString *filePathName = @"/Users/Shared/array.txt";

NOTE: This recipe assumes that you are trying this from a Mac app. iOS file references work differently; see Recipe 2.5 for examples of how to get iOS file references.

Now you can use the writeToFile:atomically: method to write the contents of this array to the Mac’s file system.

[listOfObjects writeToFile:filePathName
                atomically:YES];

See Listing 3-12 for the code.

The Code

Listing 3-12. main.m

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[])
{

    @autoreleasepool {

        NSArray *listOfObjects = [NSArray arrayWithObjects:@"A", @"B", @"C", [NSNumberImage
   numberWithInt:1], [NSNumber numberWithInt:2], [NSNumber numberWithInt:3],  nil];

        NSString *filePathName = @"/Users/Shared/array.txt";

        [listOfObjects writeToFile:filePathName
                        atomically:YES];

    }

    return 0;
}

Usage

To use this code, build and run your Mac app from Xcode. Use Finder to locate the file that was created, which will be at /Users/Shared/array.txt. Here is what the contents of the text file will look like:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"Image
 "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
        <string>A</string>
        <string>B</string>
        <string>C</string>
        <integer>1</integer>
        <integer>2</integer>
        <integer>3</integer>
</array>
</plist>

The data is organized in XML format as a property list (an Objective-C format to store keyed data).

3.9 Reading Arrays from the File System

Problem

You have files available to your app that contain content organized like an array and you want to use this content in your application.

Solution

If you have a file from an array that was saved using the writeToFile:atomically: method, use the initWithContentsOfFile: constructor to instantiate a new array populated with the contents from the file.

How It Works

For this recipe, use the file from Recipe 3.8 where you saved the contents of an array to the file system. So, use the same file path name here:

NSString *filePathName = @"/Users/Shared/array.txt";

NOTE: This recipe assumes that you are trying this from a Mac app. iOS file references work differently; see Recipe 2.5 for examples of how to get iOS file references.

Once you have that, you can use the initWithContentsOfFile: constructor to create a new array populated with the content from the file.

NSArray *listOfObjects = [[NSArray alloc] initWithContentsOfFile:filePathName];

See Listing 3-13 for the code.

The Code

Listing 3-13. main.m

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[])
{

    @autoreleasepool {

        NSString *filePathName = @"/Users/Shared/array.txt";

        NSArray *listOfObjects = [[NSArray alloc] initWithContentsOfFile:filePathName];

        NSLog(@"%@", listOfObjects);

    }
    return 0;
}

Usage

To use this code, build and run your Mac app from Xcode. Inspect the log to see the contents of the array.

(
    A,
    B,
    C,
    1,
    2,
    3
)

3.10 Creating a Dictionary

Problem

Your app requires you to group objects together in a list and you want to be able to reference the objects using a key.

Solution

Objective-C has two Foundation classes named NSDictionary and NSMutableDictionary that you can use to create lists of objects with keys. Use NSDictionary when you want a list that you know you won’t need to change on the fly and NSMutableDictionary when you know you will need to add and remove objects from the dictionary later.

How It Works

Dictionaries are created in Objective-C like other objects: you can use the alloc and init constructors or convenience functions like dictionaryWithObjects:forKeys: to create the dictionary. If you use NSDictionary to create your dictionary, you can’t make any changes to the dictionary once the dictionary is created. Use NSMutableDictionary to create dictionaries that you can later modify.

Here is an example of creating a dictionary that contains Hello World in different languages. Each version of the phrase is keyed to its language.

NSArray *listOfObjects = [NSArray arrayWithObjects:@"Hello World", @"Bonjour tout leImage
 monde", @"Hola Mundo", nil];

NSArray *listOfKeys = [NSArray arrayWithObjects:@"english", @"french", @"spanish", nil];

NSDictionary *dictionary2 = [NSDictionary dictionaryWithObjects:listOfObjects
                                                        forKeys:listOfKeys];

The NSDictionary constructor arrayWithObjects:forKeys: requires two arrays as parameters. The first array must contain the objects to be stored while the second array must contain the keys associated with the objects.

If you choose to use NSMutableDictionary, you can use the same constructors to create your arrays (NSMutableDictionary is a subclass of NSDictionary). You can also create your NSMutableDictionary by using alloc and init since you will likely add objects to your array at some future point. See Table 3-3 for a complete list of available constructors for NSDictionary and NSMutableDictionary and Listing 3-14 for the code.

Image

Image

The Code

Listing 3-14. main.m

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[])
{

    @autoreleasepool {

        NSDictionary *dictionary1 = [[NSDictionary alloc] init];

        NSArray *listOfObjects = [NSArray arrayWithObjects:@"Hello World",  Image
@"Bonjour tout le monde",  @"Hola Mundo", nil];

        NSArray *listOfKeys = [NSArray arrayWithObjects:@"english", @"french",Image
 @"spanish", nil];

        NSDictionary *dictionary2 = [NSDictionary dictionaryWithObjects:listOfObject
                                                                forKeys:listOfKeys];

        NSLog(@"dictionary2 = %@", dictionary2);

    }
    return 0;
}

Usage

To use this code, build and run your Mac app from Xcode. You can set a breakpoint and use the Xcode debugger to inspect the contents of these dictionaries. In the next recipe, you will see how to reference each of these dictionary elements so you can print out their contents to the log or use them elsewhere in your programs. You can see the entire contents of the dictionary printed out in your log.

dictionary2 = {
    english = "Hello World";
    french = "Bonjour tout le monde";
    spanish = "Hola Mundo";
}

3.11 Referencing Objects in Arrays

Problem

You would like to get references to the objects in your dictionaries to either access their properties or to send messages to the objects.

Solution

Use the objectForKey: method to get a reference to the object referenced by the key that you supply.

How It Works

NSDictionary objects keep lists of objects organized based on the keys that you provide. This makes it very easy and fast to look up any object of interest. Simply use objectForKey: and provide the key for the object that you want to look up to get the reference that you need.

NSString *helloWorld = [dictionary objectForKey:@"english"];

See Listing 3-15 for the code.

The Code

Listing 3-15. main.m

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[])
{

    @autoreleasepool {

        NSArray *listOfObjects = [NSArray arrayWithObjects:@"Hello World", Image
@"Bonjour tout le monde", @"Hola Mundo", nil];

        NSArray *listOfKeys = [NSArray arrayWithObjects:@"english", @"french",Image
 @"spanish", nil];

        NSDictionary *dictionary = [NSDictionary dictionaryWithObjects:listOfObjects
                                                               forKeys:listOfKeys];

        NSString *helloWorld = [dictionary objectForKey:@"english"];

        NSLog(@"%@", helloWorld);

    }
    return 0;
}

Usage

To use this code, build and run your Mac app from Xcode. The hello world message that prints out is the one keyed to English.

Hello World

To see the hello world message in French, add this code to your application:

helloWorld = [dictionary objectForKey:@"french"];

NSLog(@"%@", helloWorld);

Run the app again and take a look at the last console message to see the French hello world message. You can do the same for Spanish as well.

3.12 Obtaining the Dictionary Count

Problem

Your app is working with the content in your dictionaries and you need to know how many elements are in the dictionary to present your content appropriately.

Solution

NSDictionary objects have a count property that you can use to find out how many elements are in the dictionary.

How It Works

To use the count property, you can use dot notation (dictionary.count) on any dictionary object or you can send the count message ([dictionary count]) to find out how many elements are in the dictionary. See Listing 3-16 for the code.

The Code

Listing 3-16. main.m

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[])
{

    @autoreleasepool {

        NSArray *listOfObjects = [NSArray arrayWithObjects:@"Hello World", Image
@"Bonjour tout le monde", @"Hola Mundo", nil];

        NSArray *listOfKeys = [NSArray arrayWithObjects:@"english", @"french",Image
 @"spanish", nil];

        NSDictionary *dictionary = [NSDictionary dictionaryWithObjects:listOfObjects
                                                               forKeys:listOfKeys];
        NSUInteger count = dictionary.count;

        NSLog(@"The dictionary contains %lu items", count);

    }
    return 0;
}

Usage

To use this code, build and run your Mac app from Xcode. The log message will present the number of elements.

The dictionary contains 3 items

3.13 Iterating Through a Dictionary

Problem

You have a dictionary of objects and you would like to be able to send the same message or access the same property for every object in the dictionary.

Solution

Use the allValues NSDictionary function to convert the dictionary to an array that you can use with a for-each loop. Or use enumerateKeysAndObjectsUsingBlock: to work with each object in the dictionary.

How It Works

NSDictionary objects come with one built-in way to iterate through a list of objects. However, you can temporarily convert the dictionary key and object contents to arrays if you would rather use the methods described in Recipe 3.4. For instance, to iterate through the objects in a dictionary using a for-each loop, you could do something like this:

for (NSString *s in [dictionary allValues]) {
    NSLog(@"value: %@", s);
}

The allValues NSDictionary function is what gives you the objects organized like an array instead of a dictionary. There is also an allKeys function that gives you all the key values as an array.

for (NSString *s in [dictionary allKeys]) {
    NSLog(@"key: %@", s);
}

You can also use blocks to execute code for each object in a dictionary by using the enumerateKeysAndObjectsUsingBlock: method. You can use this to define a block of code that will be applied to each object in the dictionary without setting up a for-each loop or getting references to the array version of the dictionary.

[dictionary enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
    NSLog(@"key = %@ and obj = %@", key, obj);
}];

See Listing 3-17 for the code.

The Code

Listing 3-17. main.m

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[])
{

    @autoreleasepool {

        NSArray *listOfObjects = [NSArray arrayWithObjects:@"Hello World", Image
@"Bonjour tout le monde", @"Hola Mundo", nil];

        NSArray *listOfKeys = [NSArray arrayWithObjects:@"english", @"french",Image
 @"spanish", nil];

        NSDictionary *dictionary = [NSDictionary dictionaryWithObjects:listOfObjects
                                                               forKeys:listOfKeys];

        for (NSString *s in [dictionary allValues]) {
            NSLog(@"value: %@", s);
        }

        for (NSString *s in [dictionary allKeys]) {
            NSLog(@"key: %@", s);
        }

        [dictionary enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
            NSLog(@"key = %@ and obj = %@", key, obj);
        }];

    }
    return 0;
}

Usage

To use this code, build and run your Mac app from Xcode. The log message will present the results of each way of iterating through the dictionary.

value: Hello World
value: Bonjour tout le monde
value: Hola Mundo
key: english
key: french
key: spanish
key = english and obj = Hello World
key = french and obj = Bonjour tout le monde
key = spanish and obj = Hola Mundo

3.14 Manipulating Dictionary Contents

Problem

You want your dictionary content to be more dynamic so that you or users can add, remove, and insert objects into dictionaries. However, NSDictionary is an immutable class, so once you create an NSDictionary you can’t make any changes to its contents.

Solution

When you know that your dictionary needs to be dynamic, use NSMutableDictionary. It is a subclass of NSDictionary, which means that you can work with NSMutableDictionary as you would with NSDictionary. But NSMutableDictionary gives you methods that let you add, remove, and insert objects into the dictionary.

How It Works

First, you must instantiate an NSMutableDictionary object. You can use any constructor to do this. To create a new empty NSMutableDictionary, you may simply use alloc and init.

NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];

To add objects to this array, you must send the setObject:forKey: message to the dictionary with the object that you are adding and the key that goes with the object.

[dictionary setObject:@"Hello World"
               forKey:@"english"];

[dictionary setObject:@"Bonjour tout le monde"
               forKey:@"french"];

[dictionary setObject:@"Hola Mundo
               forKey:@"spanish"];

When you use setObject:forKey you are always adding objects to the dictionary indexed by the key that you provide.

To remove an object from a dictionary, you must have the key that is matched to the object. If you have the key, you can use the removeObjectForKey: method to remove an object.

[dictionary removeObjectForKey:@"french"];

Finally, you can remove all the objects from the dictionary at once by using the removeAllObjects method. See Listing 3-18 for the code.

The Code

Listing 3-18. main.m

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[])
{

    @autoreleasepool {

        NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];

        [dictionary setObject:@"Hello World"
                       forKey:@"english"];

        [dictionary setObject:@"Bonjour tout le monde"
                       forKey:@"french"];

        [dictionary setObject:@"Hola Mundo"
                       forKey:@"spanish"];

        NSLog(@"OBJECTS ADDED TO DICTIONARY: %@", dictionary);

        [dictionary removeObjectForKey:@"french"];

        NSLog(@"OBJECT REMOVED FROM DICTIONARY: %@", dictionary);

        [dictionary removeAllObjects];

        NSLog(@"ALL OBJECTS REMOVED FROM DICTIONARY: %@", dictionary);

    }
    return 0;
}

Usage

To use this code, build and run your Mac app from Xcode. Check the log console to see what happens to the dictionary after each operation is applied.

OBJECTS ADDED TO DICTIONARY: {
    english = "Hello World";
    french = "Bonjour tout le monde";
    spanish = "Hola Mundo";
}
OBJECT REMOVED FROM DICTIONARY: {
    english = "Hello World";
    spanish = "Hola Mundo";
}
ALL OBJECTS REMOVED FROM DICTIONARY: {
}

3.15 Saving Dictionaries to the File System

Problem

You want to save the objects in your dictionary to the file system to be used later on or by another program.

Solution

If your dictionary contains lists of number or string objects, you can save all of these to your file system to be used later. Use the writeToFile:atomically: method to do this. Note that this does not work with custom objects.

How It Works

For this recipe, set up a dictionary with phrases matched to keys.

NSArray *listOfObjects = [NSArray arrayWithObjects:@"Hello World", Image
@"Bonjour tout le monde", @"Hola Mundo", nil];

NSArray *listOfKeys = [NSArray arrayWithObjects:@"english", @"french", @"spanish", nil];

NSDictionary *dictionary = [NSDictionary dictionaryWithObjects:listOfObjects
                                                       forKeys:listOfKeys];

To save this to the file system, you first need a file reference.

NSString *filePathName = @"/Users/Shared/dictionary.txt";

NOTE: This recipe assumes that you are trying this from a Mac app. iOS file references work differently; see Recipe 2.5 for examples of how to get iOS file references.

Now you can use the writeToFile:atomically: method to write the contents of this dictionary to the Mac’s file system.

[dictionary writeToFile:filePathName
                         atomically:YES];

See Listing 3-19 for the code.

The Code

Listing 3-19. main.m

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[])
{

    @autoreleasepool {

        NSArray *listOfObjects = [NSArray arrayWithObjects:@"Hello World", Image
        @"Bonjour tout le monde", @"Hola Mundo", nil];

        NSArray *listOfKeys = [NSArray arrayWithObjects:@"english", @"french",Image
 @"spanish", nil];

        NSDictionary *dictionary = [NSDictionary dictionaryWithObjects:listOfObjects
                                                               forKeys:listOfKeys];

        NSString *filePathName = @"/Users/Shared/dictionary.txt";

        [dictionary writeToFile:filePathName
                     atomically:YES];

    }
    return 0;
}

Usage

To use this code, build and run your Mac app from Xcode. Use Finder to locate the file that was created (which will be at /Users/Shared/dictionary.txt). Here is what the contents of the text file will look like:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"Image
 "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <key>english</key>
        <string>Hello World</string>
        <key>french</key>
        <string>Bonjour tout le monde</string>
        <key>spanish</key>
        <string>Hola Mundo</string>
</dict>
</plist>

The data is organized in XML format as a property list (an Objective-C format to store keyed data).

3.16 Reading Dictionaries from the File System

Problem

You have files available to your app that contain content organized like a dictionary and you want to use this content in your application.

Solution

If you have a file from a dictionary that was saved using the writeToFile:atomically: method, use the initWithContentsOfFile: constructor to instantiate a new dictionary populated with the contents from the file.

How It Works

For this recipe, use the file from Recipe 3.15 with the contents of that dictionary on the file system. So, you can use the same file path name here:

NSString *filePathName = @"/Users/Shared/dictionary.txt";

NOTE: This recipe assumes that you are trying this from a Mac app. iOS file references work differently; see Recipe 2.5 for examples of how to get iOS file references.

Once you have that, you can use the initWithContentsOfFile: constructor to create a new dictionary populated with the content from the file.

NSDictionary *dictionary = [[NSDictionary alloc] initWithContentsOfFile:filePathName];

See Listing 3-20 for the code.

The Code

Listing 3-20. main.m

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[])
{

    @autoreleasepool {

        NSString *filePathName = @"/Users/Shared/dictionary.txt";

        NSDictionary *dictionary = [[NSDictionary alloc]Image
 initWithContentsOfFile:filePathName];

        NSLog(@"dictionary: %@", dictionary);

    }
    return 0;
}

Usage

To use this code, build and run your Mac app from Xcode. Inspect the log to see the contents of the dictionary.

dictionary: {
    english = "Hello World";
    french = "Bonjour tout le monde";
    spanish = "Hola Mundo";
}

3.17 Creating a Set

Problem

Your app requires you to group objects together in an unordered collection or a set.

Solution

Objective-C has two Foundation classes named NSSet and NSMutableSet that you can use to create sets. Use NSSet when you have a set that you know you won’t need to change on the fly and NSMutableSet when you know you will need to add and remove objects from the set later.

How It Works

Sets are created in Objective-C like other objects: use the alloc and init constructors or convenience functions like setWithObjects: to create the set. If you use NSSet to create your set, you can’t make any changes to the dictionary once the set is created. Use NSMutableSet to create sets that you can later modify.

Here is an example of creating a set that contains Hello World in different languages:

NSSet *set = [NSSet setWithObjects:@"Hello World", @"Bonjour tout le monde", @"HolaImage
 Mundo", nil];

The NSSet constructor setWithObjects: requires a nil-terminated array with the objects that will appear in the set.

If you choose to use NSMutableSet, you can use the same constructors to create your sets (NSMutableSet is a subclass of NSSet). You can also create your NSMutableSet by using alloc and init since you will likely add objects to your set at some future point. See Table 3-4 for a complete list of available constructors for NSSet and NSMutableSet and Listing 3-21 for the code.

Image

The Code

Listing 3-21. main.m

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[])
{

    @autoreleasepool {

        NSSet *set = [NSSet setWithObjects:@"Hello World", @"Bonjour tout le monde", Image
@"Hola Mundo", nil];

        NSLog(@"set: %@",set);

    }
    return 0;
}

Usage

To use this code, build and run your Mac app from Xcode. In your log you can see the entire contents of the set printed out.

set: {(
    "Bonjour tout le monde",
    "Hello World",
    "Hola Mundo"
)}

3.18 Obtaining the Set Count

Problem

Your app is working with the content in your sets and you need to know how many elements are in the set to present your content appropriately.

Solution

NSSet objects have a count property that you can use to find out how many elements are in the set.

How It Works

To use the count property, you can use dot notation (set.count) on any set object or you can send the count message ([set count]) to find out how many elements are in the set. See Listing 3-22 for the code.

The Code

Listing 3-22. main.m

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[])
{

    @autoreleasepool {

        NSSet *set = [NSSet setWithObjects:@"Hello World", @"Bonjour tout le monde", Image
@"Hola Mundo", nil];

        NSUInteger count = set.count;

        NSLog(@"The set contains %lu items", count);

    }
    return 0;
}

Usage

To use this code, build and run your Mac app from Xcode. The log message will present the number of elements.

The set contains 3 items

3.19 Comparing Sets

Problem

You work with many sets in your app and you would like to find out more information about each set and what objects are in each of your sets.

Solution

NSSet comes with some built-in methods that you can use to compare sets. You can find out if two sets intercept (they have some elements in common). You can find out if one set is a subset of another (one set is composed of objects that are entirely in another set). You can also find out if one set is equal to another or if an object is already in a set.

How It Works

For this recipe, you need two sets. These sets should simply have string objects for letters in the alphabet.

NSSet *set1 = [NSSet setWithObjects:@"A", @"B", @"C", @"D", @"E", nil];

NSSet *set2 = [NSSet setWithObjects:@"D", @"E", @"F", @"G", @"H", nil];

If you want to see if these sets have objects that overlap (the sets intersect), you can use the intersectsSet: function to return a BOOL.

BOOL setsIntersect = [set1 intersectsSet:set2];

To find out if one set contains objects that are entirely in another set, use the isSubsetOfSet: function.

BOOL set2IsSubset = [set2 isSubsetOfSet:set1];

To test whether two sets are identical, use the isEqualToSet: function.

BOOL set1IsEqualToSet2 = [set1 isEqualToSet:set2];

Finally, if you want to know whether an object is already in a set, use containsObject: to find out.

BOOL set1ContainsD = [set1 containsObject:@"D"];

See Listing 3-23 for the code.

The Code

Listing 3-23. main.m

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[])
{

    @autoreleasepool {

        NSSet *set1 = [NSSet setWithObjects:@"A", @"B", @"C", @"D", @"E", nil];

        NSSet *set2 = [NSSet setWithObjects:@"D", @"E", @"F", @"G", @"H", nil];

        NSLog(@"set1 contains:%@", set1);

        NSLog(@"set2 contains:%@", set2);

        BOOL setsIntersect = [set1 intersectsSet:set2];

        BOOL set2IsSubset = [set2 isSubsetOfSet:set1];

        BOOL set1IsEqualToSet2 = [set1 isEqualToSet:set2];

        BOOL set1ContainsD = [set1 containsObject:@"D"];

        NSLog(@"setsIntersect = %i, set2IsSubset = %i, set1IsEqualToSet2 = %i,Image
 set1ContainsD = %i", setsIntersect, set2IsSubset, set1IsEqualToSet2, set1ContainsD);


    }
    return 0;
}

Usage

To use this code, build and run your Mac app from Xcode. The log message will present the sets and the results of the various tests. The log will print out 1 when a BOOL is YES and 0 when a BOOL is NO.

set1 contains:{(
    A,
    D,
    B,
    E,
    C
)}
set2 contains:{(
    H,
    F,
    D,
    G,
    E
)}
setsIntersect = 1, set2IsSubset = 0, set1IsEqualToSet2 = 0, set1ContainsD = 1

3.20 Iterating Through a Set

Problem

You have a set of objects and you would like to be able to send the same message or access the same property for every object in the set.

Solution

Use the allObjects NSSet function to convert the set to an array and then you can use a for-each loop. Or use enumerateObjectsUsingBlock: to work with each object in the set. NSSet also supports makeObjectsPerformSelector:, which is great when you specifically want each object to execute just one method.

How It Works

You can temporarily convert the set contents to an array if you would like to use the methods described in Recipe 3.4. For instance, to iterate through the objects in a set using a for-each loop, you could do something like this:

for (NSString *s in [set allObjects]) {
    NSLog(@"value: %@", s);
}

You can also use blocks to execute code for each object in a set by using the enumerateObjectsUsingBlock: method. You can use this to define a block of code that will be applied to each object in the dictionary without setting up a for-each loop or getting references to the array version of the set.

[set enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
    NSLog(@"obj = %@", obj);
}];

If you want to simply perform one action on each object and that action is a method coded in the object’s class definition, you can use makeObjectsPerformSelector:.

[set makeObjectsPerformSelector:@selector(description)];

See Listing 3-24 for the code.

The Code

Listing 3-24. main.m

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[])
{

    @autoreleasepool {

        NSSet *set = [NSSet setWithObjects:@"Hello World", @"Bonjour tout le monde", Image
@"Hola Mundo", nil];

        for (NSString *s in [set allObjects]) {
            NSLog(@"value: %@", s);
        }

        [set enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
            NSLog(@"obj = %@", obj);
        }];

        [set makeObjectsPerformSelector:@selector(description)];

    }
    return 0;
}

Usage

To use this code, build and run your Mac app from Xcode. The log message will present the results of each way of iterating through the set.

value: Bonjour tout le monde
value: Hello World
value: Hola Mundo
obj = Bonjour tout le monde
obj = Hello World
obj = Hola Mundo

3.21 Manipulating Set Contents

Problem

You want your set contents to be more dynamic so that you or your users can add objects to sets and remove objects from sets. However, NSSet is an immutable class, so once you create an NSSet you can’t make any changes to its contents.

Solution

When you know that your set needs to be dynamic, use NSMutableSet. It is a subclass of NSSet and so you can work with NSMutableArray as you would with NSSet. But NSMutableSet provides methods to add and remove objects.

How It Works

First, instantiate a NSMutableSet object. You can use any constructor to do this. To create a new empty NSMutableSet, you can use alloc and init.

NSMutableSet *set = [[NSMutableSet alloc] init];

To add objects to this set, you must send the addObject: message to the set with the object that you are adding as a parameter.

[set addObject:@"Hello World"];

[set addObject:@"Bonjour tout le monde"];

[set addObject:@"Hola Mundo"];

To remove an object from a set, you must already have a reference to the object. If you have this, then you can use removeObject.

[set removeObject:@"Bonjour tout le monde"];

Finally, you can remove all the objects from the set at once by using the removeAllObjects method. See Listing 3-25 for the code.

The Code

Listing 3-25. main.m

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[])
{

    @autoreleasepool {

        NSMutableSet *set = [[NSMutableSet alloc] init];

        [set addObject:@"Hello World"];

        [set addObject:@"Bonjour tout le monde"];

        [set addObject:@"Hola Mundo"];

        NSLog(@"Objects added to set:%@", set);

        [set removeObject:@"Bonjour tout le monde"];

        NSLog(@"Object removed from set:%@", set);

        [set removeAllObjects];

        NSLog(@"All objects removed from set:%@", set);

    }
    return 0;
}

Usage

To use this code, build and run your Mac app from Xcode. Check the console to see what happens to the set after each operation is applied.

Objects added to set:{(
    "Bonjour tout le monde",
    "Hello World",
    "Hola Mundo"
)}
Object removed from set:{(
    "Hello World",
    "Hola Mundo"
)}
All objects removed from set:{(
)}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.24.106