Use NSArray
and NSMutableArray
classes to store objects into
arrays that are fixed and that you can change, respectively.
An object of type NSArray
or
any of its subclasses has the capability to store
n number of other objects, where
n will be determined by the runtime and is
influenced by how much memory is available at the time. These objects
can then be accessed using their index. For instance, let’s say you have
10 pairs of socks. Now imagine placing them all on a flat surface from
left to right and calling them socks 1, socks 2, socks 3, and so on. So
the leftmost pair of socks is now addressed as socks 1, the pair next to
it is called socks 2, and the rightmost pair is called socks 10. Isn’t
that easier than saying something like “the blue socks next to my red
socks”? That’s exactly what arrays do: they make arranging items much
easier.
Note
You can place any object of type NSObject
or any of its
subclasses into an array of type NSArray
(or subclasses of that type). An
array can contain a mix of different types of objects. Not all objects
have to be of the same type. In other words, you can have one array
with strings, numbers, dictionaries, or even other arrays inside it.
Arrays can contain any object as long as those objects can be wrapped
in the id
data type wrapper.
The primary difference between NSArray
and NSMutableArray
is that a mutable array can be changed/modified after it has been
allocated and initialized, whereas an immutable array, NSArray
, cannot.
Let’s have a look at an example. First, create an instance of
NSString
and two instances of
NSNumber
and place them in an
immutable array:
NSArray
*
array
=
@
[
@"My String"
,
@
123
,
@
-
123
];
NSLog
(
@"array = %@"
,
array
);
When you run this program, the following text is printed to your console:
array = ( "My String", 123, "-123" )
We used the new collection subscripting features of our LLVM compiler to construct
the array. These let us construct the array using the @[]
collection format and place our objects
between the opening and the closing square brackets. This syntax creates
an instance of an array for us. When using this method of constructing
your arrays, pass your objects that need to be placed inside the array
one by one.
We can also use the arrayWithObjects:
class method of NSArray
to create an autorelease array, like
so:
NSArray
*
array
=
[
NSArray
arrayWithObjects:
stringObject
,
signedNumber
,
unsignedNumber
,
nil
];
You can call the count
method on your array to get the number of objects in that array.
You can go through your array using a for loop or using an enumerator.
Let’s have a look at the solution with a for loop first:
NSArray
*
array
=
@
[
@"My String"
,
@
123
,
@
-
123
];
NSUInteger
counter
=
0
;
for
(
counter
=
0
;
counter
<
[
array
count
];
counter
++
){
id
object
=
array
[
counter
];
NSLog
(
@"Object = %@"
,
object
);
}
And here is the output:
Object = My String Object = -123 Object = 123
Aside from the []
syntax to access a specific object in an array, we can also use
the objectAtIndex:
method to get an
object at a specific index. Remember that indexes are zero based. In
other words, when the counter reaches -1, the loop has to stop because
there can be no negative indexes in an array.
As mentioned before, you can also use fast enumeration to go through objects of an array. Fast enumeration is a language feature in Objective-C that allows you to enumerate objects in an array or dictionary (or any other object that supports fast enumeration) without having to use any counter or for loop. The format is as follows:
for
(
Type
variableName
in
array
/
dictionary
/
etc
){
...
}
Suppose we want to code the previous example without the overhead of a counter variable. Here is how we can do it using fast enumeration:
for
(
id
object
in
array
){
NSLog
(
@"Object = %@"
,
object
);
}
The results are practically identical to the results we got from the previous version of this code that used a counter variable.
Mutable arrays are very interesting. As you probably have already guessed, immutable arrays cannot be modified once allocated and initialized. Mutable arrays, however, can be modified after their allocation and initialization. Let’s have a look at an example:
NSArray
*
anotherArray
=
@
[
@"String 1"
,
@"String 2"
,
@"String 3"
];
NSMutableArray
*
mutableArray
=
[
NSMutableArray
arrayWithArray:
@
[
@"My String"
,
@
123
,
@
-
123
]];
[
mutableArray
addObject:
@
123
];
[
mutableArray
removeObject:
@
-
123
];
[
mutableArray
addObjectsFromArray:
anotherArray
];
for
(
id
object
in
mutableArray
){
NSLog
(
@"Object = %@"
,
object
);
}
Before we go into analyzing the code, let’s have a look at its output:
Object = My String Object = 123 Object = 123 Object = String 1 Object = String 2 Object = String 3
You might be wondering what just happened. Well, let’s have a look at what methods of the NSMutableArray
class we actually used:
addObject:
This method allows us to add an object to the end of a mutable array.
removeObject:
Using this method, we can remove a specific object from the array. Remember that we pass an object to this method, not an index of the object. To remove an object using an index into the array, we must use the
removeObjectAtIndex:
method.addObjectsFromArray:
With this method, we can add objects from one array (either mutable or immutable) into our mutable array.
Warning
Bear in mind that during fast enumeration of a mutable array, you must not add
to or remove anything from that array or you will get a runtime error.
This is the default behavior of mutable arrays during fast
enumeration. There are two ways of avoiding this. Either simply follow
the rule of not modifying an array while fast enumerating it, or, if
you prefer the more proactive approach, you can subclass NSMutableArray
and change the behavior for
yourself. This topic is outside the scope of this book and will not be
discussed.
If you are interested in block objects (and we’ll see good reasons to be, later in the
book, in Chapter 6),
you can also enumerate objects in your arrays using the enumerate
ObjectsUsingBlock:
method. The block object
passed to this method should:
Return no value.
Have three parameters:
First parameter of type
id
, which will be the object being enumerated at each loop of enumeration.Second parameter of type
NSUInteger
, which will tell you the index of the current object being enumerated.Last but not least, a parameter of type
*BOOL
, which you can use to stop the enumeration. This is a pointer to a boolean variable, which should beNO
as long as you want the enumeration to proceed. You can change the value of this pointer toYES
in order to stop the enumeration at any time. You would use this if you are looking for an object in an array and you would like to stop the enumeration as soon as you’ve found that object, since there is no point continuing the enumeration if you’ve already found your object.
NSArray
*
myArray
=
@
[
@"String 1"
,
@"String 2"
,
@"String 3"
,
@"String 4"
];
[
myArray
enumerateObjectsUsingBlock:
^
(
__strong
id
obj
,
NSUInteger
idx
,
BOOL
*
stop
)
{
NSLog
(
@"Object = %@"
,
obj
);
}];
If you need to sort an array, simply use the new block-based sorting methods of NS
Array
or NSMutableArray
. Just remember that the sorting
methods of NSArray
return a new
instance of NSArray
and leave the
original array intact, since NSArray
cannot be modified (sorting can modify an array) after it has been
allocated and initialized. This is in comparison to the sorting methods
of NSMutableArray
, where the original
array will be the target of sorting and the sorting methods will not
return a new array. Let’s look at sorting a mutable array:
NSMutableArray
*
myArray
=
[
NSMutableArray
arrayWithArray:
@
[
@"String 2"
,
@"String 4"
,
@"String 1"
,
@"String 3"
]];
[
myArray
sortUsingComparator:
^
NSComparisonResult
(
__strong
id
obj1
,
__strong
id
obj2
)
{
NSString
*
string1
=
(
NSString
*
)
obj1
;
NSString
*
string2
=
(
NSString
*
)
obj2
;
return
[
string1
compare:
string2
];
}];
NSLog
(
@"myArray = %@"
,
myArray
);
NSLog
(
@"%d"
,
[
@"String 3"
compare:
@"String 1"
]);
The results will then be printed to the console, as follows:
myArray = ( "String 1", "String 2", "String 3", "String 4" )
So, what happened? We simply called the sortUsingComparator:
method of our array. This
method takes in a block object (marked by the initial ^
character) that has to return a value of
type NSComparisonResult
. This value
can be any of the following:
NSOrderedSame
The two values being compared are equal.
NSOrderedAscending
The value on the left of the comparison is smaller than the value on the right. Think of it as this: transition from value 1 (left) to value 2 (right) is ascending, meaning that value 1 is smaller.
NSOrderedDescending
The value on the right is smaller than the value on the left. In other words, the transition from value 1 (left) to value 2 (right) is descending, meaning that value 1 is bigger than value 2.
So if we get String 3
as value 1 (left) and
String 1
as value 2 (right), the sort
function compares the two S characters and finds
them the same, then the two t characters, and so
on. Finally, when the sort function reaches the 3 and the 1, it finds
that 1 is lower than 3 in the UTF-8 string character set, and therefore
that the second element is lower than the first.
The block object submitted to the sortUsingComparator:
method takes two
parameters:
- First object of type
id
This is the first object in the comparison in each iteration.
- Second object of type
id
This is the second object in the comparison in each iteration.
So when sorting the array, simply use a block-based approach. It’s the way Apple is pushing developers to go forward with their implementations, so it’s good to know about block objects.
Get iOS 6 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.