- •C and Objective-C
- •How this book works
- •How the life of a programmer works
- •Installing Apple’s developer tools
- •Getting started with Xcode
- •Where do I start writing code?
- •How do I run my program?
- •So what is a program?
- •Don’t stop
- •Types
- •A program with variables
- •Challenge
- •Boolean variables
- •When should I use a function?
- •How do I write and use a function?
- •How functions work together
- •Local variables, frames, and the stack
- •Recursion
- •Looking at the frames in the debugger
- •return
- •Global and static variables
- •Challenge
- •printf()
- •Integer operations
- •Integer division
- •Operator shorthand
- •Floating-point numbers
- •Tokens for displaying floating-point numbers
- •The while loop
- •The for loop
- •break
- •continue
- •The do-while loop
- •Challenge
- •Getting addresses
- •Storing addresses in pointers
- •Getting the data at an address
- •How many bytes?
- •NULL
- •Stylish pointer declarations
- •Challenges
- •Writing pass-by-reference functions
- •Avoid dereferencing NULL
- •Creating and using your first object
- •Message anatomy
- •Objects in memory
- •Challenge
- •Nesting message sends
- •Multiple arguments
- •Sending messages to nil
- •Challenge
- •Challenge
- •NSMutableArray
- •Reference pages
- •Quick Help
- •Other options and resources
- •Accessor methods
- •Dot notation
- •Properties
- •self
- •Multiple files
- •Challenge
- •Overriding methods
- •super
- •Challenge
- •Object ownership and ARC
- •Creating the Asset class
- •Adding a to-many relationship to Employee
- •Challenge
- •Retain cycles
- •Weak references
- •Zeroing of weak references
- •For the More Curious: Manual reference counting and ARC History
- •Retain count rules
- •NSArray/NSMutableArray
- •Immutable objects
- •Sorting
- •Filtering
- •NSSet/NSMutableSet
- •NSDictionary/NSMutableDictionary
- •Preprocessor directives
- •#include and #import
- •#define
- •Global variables
- •enum
- •#define vs global variables
- •Writing an NSString to a file
- •Reading files with NSString
- •Writing an NSData object to a file
- •Reading an NSData from a file
- •Target-action
- •Helper objects
- •Notifications
- •Which to use?
- •Callbacks and object ownership
- •Challenge
- •Getting started with iTahDoodle
- •BNRAppDelegate
- •Adding a C helper function
- •Objects in iTahDoodle
- •Model-View-Controller
- •The application delegate
- •Setting up views
- •Running on the iOS simulator
- •Wiring up the table view
- •Adding new tasks
- •Saving task data
- •For the More Curious: What about main()?
- •Edit BNRDocument.h
- •A look at Interface Builder
- •Edit BNRDocument.xib
- •Making connections
- •Revisiting MVC
- •Edit BNRDocument.m
- •Writing init methods
- •A basic init method
- •Using accessors
- •init methods that take arguments
- •Deadly init methods
- •Property attributes
- •Mutability
- •Lifetime specifiers
- •copy
- •More about copying
- •Advice on atomic vs. nonatomic
- •Key-value coding
- •Non-object types
- •Defining blocks
- •Using blocks
- •Declaring a block variable
- •Assigning a block
- •Passing in a block
- •typedef
- •Return values
- •Memory management
- •The block-based future
- •Challenges
- •Anonymous block
- •NSNotificationCenter
- •Bitwise-OR
- •Bitwise-AND
- •Other bitwise operators
- •Exclusive OR
- •Complement
- •Left-shift
- •Right-shift
- •Using enum to define bit masks
- •More bytes
- •Challenge
- •char
- •char *
- •String literals
- •Converting to and from NSString
- •Next Steps
- •Index
21
Collection Classes
You have already used two collection classes: NSArray and its subclass NSMutableArray. As you know, an array holds a collection of pointers to other objects. The pointers are ordered, so you use an index (an integer) to access the objects in the collection. In this chapter, we will delve deeper into arrays and look at some other collection classes: NSSet/NSMutableSet and NSDictionary/NSMutableDictionary.
NSArray/NSMutableArray
When you add an object to an array, the array claims ownership of it. When you remove the object from the array, the array gives up ownership. Open BMITime, and take a look at how you are using the employees array. If we ignore all the other stuff, you see something like this:
// Create an array of Employee objects
NSMutableArray *employees = [[NSMutableArray alloc] init]; for (int i = 0; i < 10; i++) {
// Create an instance of Employee
Employee *person = [[Employee alloc] init];
// Put the employee in the employees array [employees addObject:person];
}
[employees removeObjectAtIndex:5]; employees = nil;
You typically create an empty mutable array using alloc/init or the class method array. For example, you could have created the mutable array like this:
NSMutableArray *employees = [NSMutableArray array];
Note that the addObject: method adds the object to the end of the list. As objects are added, an array will grow as big as necessary to hold them.
Immutable objects
When you create an instance of NSArray, you assign all its objects to it when it is created. This typically looks like this:
NSArray *colors = [NSArray arrayWithObjects:@"Orange", @"Yellow", @"Green", nil];
135
Chapter 21 Collection Classes
The nil at the end is how you tell the method to stop. Thus, this colors array would only have three strings. (If you forget the nil, it will probably crash your program.)
Most beginning programmers are surprised by the existence of NSArray. Why would anyone want a list that can’t be changed? There are two reasons:
•You don’t trust the people you work with. That is, you want to let them look at an array, but you don’t want them to be able change it. A gentler approach is to give them an NSMutableArray, but tell them it is an NSArray. For example, imagine the following method:
// Returns |
an array of 30 |
odd numbers |
- (NSArray |
*)odds |
|
{ |
|
|
static |
NSMutableArray |
*odds = [[NSMutableArray alloc] init]; |
int i |
= 1; |
|
while |
([odds count] < |
30) { |
[odds addObject:[NSNumber numberWithInt:i]; |
||
i |
+= 2; |
|
}
return odds;
}
Anyone calling this method assumes it is returning an immutable NSArray. If the caller tries to add or remove items from the returned object, the compiler will issue a stern warning – even though, it is, in fact, an instance of NSMutableArray.
•The other reason is performance: an immutable object never needs to be copied. With a mutable object, you might make a private copy so that you know that no other code in the system can change it from underneath you. This is unnecessary for immutable objects. In fact, while the copy method of NSMutableArray makes a new copy of itself and returns a pointer to the new array, the copy method
of NSArray does nothing – it just returns a pointer to itself.
As a result, immutable objects are fairly common in Objective-C programming. In Foundation, there are many classes that create immutable instances: NSArray, NSString, NSAttributedString, NSData,
NSCharacterSet, NSDictionary, NSSet, NSIndexSet, and NSURLRequest. All of these have mutable
subclasses: NSMutableArray, NSMutableString, NSMutableAttributedString, etc. NSDate and
NSNumber, on the other hand, are immutable but don’t have mutable subclasses.
Sorting
There are several ways to sort an array, but the most common way is to use an array of sort descriptors. NSMutableArray has the following method:
- (void)sortUsingDescriptors:(NSArray *)sortDescriptors;
The argument is an array of NSSortDescriptor objects. A sort descriptor has the name of a property of the objects contained in the array and whether that property should be sorted in ascending or descending order. Why do we pass an array of sort descriptors? Imagine you said, “Sort this list of doctors by last name in ascending order.” What if two doctors have the same last name? You can specify “Sort by last name ascending, and if the last names are the same, sort by first name ascending, and if the first and last names are the same, sort by zip code.”
136
Filtering
Figure 21.1 Sort by lastName, then firstName, then zipCode
The property you sort on can be any instance variable or the result of any method of the object.
Let’s return to the BMITime project to see sorting in practice. In main(), just before logging the employees array, sort it by valueOfAssets. If two employees are holding assets of the same value, sort
them by employeeID. Edit main.m:
}
NSSortDescriptor *voa = [NSSortDescriptor sortDescriptorWithKey:@"valueOfAssets" ascending:YES];
NSSortDescriptor *ei = [NSSortDescriptor sortDescriptorWithKey:@"employeeID" ascending:YES];
[employees sortUsingDescriptors:[NSArray arrayWithObjects:voa, ei, nil]];
NSLog(@"Employees: %@", employees);
Build and run the program. You should see the employees list ordered correctly:
Employees: (
"<Employee 0: $0 in assets>", "<Employee 4: $0 in assets>", "<Employee 8: $0 in assets>", "<Employee 7: $34 in assets>", "<Employee 3: $68 in assets>", "<Employee 2: $119 in assets>", "<Employee 6: $119 in assets>", "<Employee 5: $136 in assets>", "<Employee 9: $136 in assets>", "<Employee 1: $153 in assets>"
Filtering
There is a class called NSPredicate. A predicate contains a statement that might be true, like “The employeeID is greater than 75.” NSMutableArray has a handy method for discarding all the objects that don’t satisfy the predicate:
- (void)filterUsingPredicate:(NSPredicate *)predicate;
NSArray has a method that creates a new array that contains all the objects that satisfy the predicate:
- (NSArray *)filteredArrayUsingPredicate:(NSPredicate *)predicate;
Imagine that you are going to reclaim all the assets given to employees who currently hold assets worth more than $70 total. Add the code near the end of main.m:
137