Objective-C has two types of array:
the C array we saw in
Manhattan,
and the
NSArray
(with its variant
NSMutableArray
)
we are about to see.
Like an
NSSet
,
an
NSArray
is a big object that contains (pointers to) little objects.
Unlike an
NSSet
, an
NSArray
keeps the elements in the original order.
See
Arrays: Ordered Collections.
The advantage of a C array is that it can hold elements of any data type:
int
s,
CGFloat
s,
structures,
pointers to objects, etc.
An
NSArray
can hold only pointers to objects.
For example, an
int
can not be held directly in an
NSArray
.
The
int
would have to be wrapped in an
NSValue
object,
and the address of the
NSValue
object would be stored in an array.
The advantage of an
NSArray
,
or at least of an
NSMutableArray
,
is that it can expand to receive additional elements.
This is what the method
addObject:
of class
NSMutableArray
does.
See the
setWithObjects:
method of class
NSSet
here
for an earlier example of a method that accepts a variable number of arguments
ending with
nil
.
I declared the variable
borough
to be a pointer to an
NSString
because I knew in advance that all the elements of the array were
NSString
s.
2013-10-30 20:33:22.987 Array[9413:a0b] boroughs.count == 5 2013-10-30 20:33:22.991 Array[9413:a0b] Bronx 2013-10-30 20:33:22.991 Array[9413:a0b] Brooklyn 2013-10-30 20:33:22.992 Array[9413:a0b] Manhattan 2013-10-30 20:33:22.993 Array[9413:a0b] Queens 2013-10-30 20:33:22.994 Array[9413:a0b] Staten Island 2013-10-30 20:33:22.995 Array[9413:a0b] The first borough is Bronx. 2013-10-30 20:33:23.002 Array[9413:a0b] The last borough is Staten Island.
boroughs
to an
NSMutableArray
.
NSMutableArray *boroughs = [[NSMutableArray alloc] init]; //born empty [boroughs addObject: @"Bronx"]; [boroughs addObject: @"Brooklyn"]; [boroughs addObject: @"Manhattan"]; [boroughs addObject: @"Queens"]; [boroughs addObject: @"Staten Island"]; //Do not add nil at the end.
familyNames
method of class
UIFont
returns another array of strings we can loop through.
Insert the following code into the
main
function of any app immediately before the return statement.
for (NSString *name in [UIFont familyNames]) { NSLog(@"%@", name); }
2013-10-30 20:47:03.517 Array[9520:a0b] Marion 2013-10-30 20:47:03.518 Array[9520:a0b] Copperplate 2013-10-30 20:47:03.519 Array[9520:a0b] Heiti SC 2013-10-30 20:47:03.520 Array[9520:a0b] Iowan Old Style 2013-10-30 20:47:03.521 Array[9520:a0b] Courier New 2013-10-30 20:47:03.522 Array[9520:a0b] Apple SD Gothic Neo 2013-10-30 20:47:03.522 Array[9520:a0b] Heiti TC 2013-10-30 20:47:03.523 Array[9520:a0b] Gill Sans 2013-10-30 20:47:03.523 Array[9520:a0b] Thonburi 2013-10-30 20:47:03.524 Array[9520:a0b] Marker Felt etc.To display a one-line sample of each font, we will use a
UIWebView
here
and
here.
//caseInsensitiveCompare: is a method of class NSString. SEL sel = @selector(caseInsensitiveCompare:); NSArray *sorted = [[UIFont familyNames] sortedArrayUsingSelector: sel]; for (NSString *name in sorted) { NSLog(@"%@", name); }
2013-10-30 20:52:48.982 Array[9534:a0b] Academy Engraved LET 2013-10-30 20:52:48.983 Array[9534:a0b] Al Nile 2013-10-30 20:52:48.984 Array[9534:a0b] American Typewriter 2013-10-30 20:52:48.984 Array[9534:a0b] Apple Color Emoji 2013-10-30 20:52:48.985 Array[9534:a0b] Apple SD Gothic Neo 2013-10-30 20:52:48.985 Array[9534:a0b] Arial 2013-10-30 20:52:48.986 Array[9534:a0b] Arial Hebrew 2013-10-30 20:52:48.986 Array[9534:a0b] Arial Rounded MT Bold 2013-10-30 20:52:48.987 Array[9534:a0b] Avenir 2013-10-30 20:52:48.987 Array[9534:a0b] Avenir Next etc.
A C programmer has a rage to telescope as much code as possible into a single expression:
for (NSString *name in [[UIFont familyNames] sortedArrayUsingSelector: @selector(caseInsensitiveCompare:)]) { NSLog(@"%@", name); }
windows
,
although the array probably contains only one window.
Every
window
(and in fact every
view)
has an
array
of
subviews,
although the array might contain only one subview.
Write nested loops to
print information about each (highest level) subview of each window.
Insert the following code into the
applicationDidFinishLaunchingWithOptions:
addSubview:
for (UIWindow *w in [UIApplication sharedApplication].windows) { CGRect f = w.frame; NSLog(@"window frame == (%g, %g), %g × %g", f.origin.x, f.origin.y, f.size.width, f.size.height); for (UIView *v in w.subviews) { f = v.frame; NSLog(@" view frame == (%g, %g), %g × %g", f.origin.x, f.origin.y, f.size.width, f.size.height); } }
2013-10-30 21:36:44.274 Touch[9653:a0b] window frame == (0, 0), 320 × 568 2013-10-30 21:36:44.276 Touch[9653:a0b] view frame == (0, 20), 320 × 548
To display the subviews of the subviews all the way down, we would need recursion. Is anyone up for this?
NSArray
cannot hold numbers.
It can hold only pointers to objects.
The workaround is to wrap each number in an
NSNumber
object
and store the address of the
NSNumber
object into the array.
The
@3.14
is an Objective-C
literal
that creates an
NSNumber
object.
NSArray *a = [NSArray arrayWithObjects: [NSNumber numberWithFloat: 3.14], //or @3.14, [NSNumber numberWithFloat: 2.71], //or @2.71, nil ]; for (NSNumber *n in a) { CGFloat x = n.floatValue; NSLog(@"%g", x); }
2013-10-30 21:38:41.099 Array[9669:a0b] 3.14 2013-10-30 21:38:41.113 Array[9669:a0b] 2.71
An
NSMutableArray
cannot hold numbers either.
We use the same workaround.
NSMutableArray *a = [[NSMutableArray alloc] init]; [a addObject: [NSNumber numberWithFloat: 3.14]]; //or [a addObject: @3.14]; [a addObject: [NSNumber numberWithFloat: 2.71]]; for (NSNumber *n in a) { CGFloat x = n.floatValue; NSLog(@"%g", x); }
NSArray *a = [NSArray arrayWithObjects: [NSNumber numberWithFloat: 3.14], [NSNumber numberWithFloat: 2.71], nil ]; //compare: is a method of class NSNumber. SEL sel = @selector(compare:); NSArray *sorted = [a sortedArrayUsingSelector: sel]; for (NSNumber *n in sorted) { CGFloat x = n.floatValue; NSLog(@"%g", x); }
2013-10-30 21:54:42.955 Array[9704:a0b] 2.71 2013-10-30 21:54:42.958 Array[9704:a0b] 3.14
NSArray
cannot hold structures.
It can hold only pointers to objects.
The workaround is to wrap each structure in an
NSValue
object
and store the address of the
NSValue
object into the array.
NSArray *a = [NSArray arrayWithObjects: [NSValue valueWithCGPoint: CGPointMake(10, 20)], [NSValue valueWithCGPoint: CGPointMake(30, 40)], nil ]; for (NSValue *v in a) { CGPoint p = v.CGPointValue; NSLog(@"(%g, %g)", p.x, p.y); }
2013-10-30 21:55:26.693 Array[9721:a0b] (10, 20) 2013-10-30 21:55:26.696 Array[9721:a0b] (30, 40)
An
NSMutableArray
cannot hold structures either.
We use the same workaround.
NSMutableArray *a = [[NSMutableArray alloc] init]; [a addObject: [NSValue valueWithCGPoint: CGPointMake(10, 20)]]; [a addObject: [NSValue valueWithCGPoint: CGPointMake(30, 40)]]; for (NSValue *v in a) { CGPoint p = v.CGPointValue; NSLog(@"(%g, %g)", p.x, p.y); }
objectAtIndex:
will throw an Objective-C
exception.
A “dylib” is a dynamic library.
//For our array of 5 boroughs, the index must be in the range //0 to 4 inclusive. 5 is out of range. NSString *borough = [boroughs objectAtIndex: 5]; NSLog(@"borough == %@", borough); //never arrive here
2013-10-31 07:42:11.491 Array[10790:a0b] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayI objectAtIndex:]: index 5 beyond bounds [0 .. 4]' *** First throw call stack: ( 0 CoreFoundation 0x0000000101886795 __exceptionPreprocess + 165 1 libobjc.A.dylib 0x00000001015e9991 objc_exception_throw + 43 2 CoreFoundation 0x000000010183efcf -[__NSArrayI objectAtIndex:] + 175 3 Array 0x0000000100001ef4 main + 916 4 libdyld.dylib 0x0000000101f147e1 start + 0 5 ??? 0x0000000000000001 0x0 + 1 ) libc++abi.dylib: terminating with uncaught exception of type NSException (lldb)
To catch and recover from the exception,
@try { NSString *borough = [boroughs objectAtIndex: 5]; NSLog(@"borough == %@", borough); //never arrive here } @catch (NSException *exception) { NSLog(@"name is %@", [exception name]); NSLog(@"%@", exception); } @finally { NSLog(@"Arrive here whether or not the exception was thrown."); }
2013-10-31 07:31:02.218 Array[10769:a0b] name is NSRangeException 2013-10-31 07:31:02.219 Array[10769:a0b] *** -[__NSArrayI objectAtIndex:]: index 5 beyond bounds [0 .. 4] 2013-10-31 07:31:02.219 Array[10769:a0b] Arrive here whether or not the exception was thrown.