An Instance Reference Is a Pointer
In Objective-C, if a variable is an instance of the class MyClass, that variable is of type MyClass* — a pointer to a MyClass. In general, in Objective-C, a reference to an instance is a pointer and the name of the data type of what’s at the far end of that pointer is the name of the instance’s class.
Instance References, Initialization, and nil
Merely declaring an instance reference’s type doesn’t bring any instance into existence. For example:
NSString* s; // only a declaration; no instance is pointed to
After that declaration, s is typed as a pointer to an NSString, but it is not in fact pointing to an NSString. You have created a pointer, but you haven’t supplied an NSString for it to point to.It is claiming to be a pointer to an NSString, and so your code might proceed to treat it as a pointer to an NSString.But it is pointing at garbage.
A pointer pointing at garbage is liable to cause serious trouble down the road when you accidentally try to use it as an instance. Sending a message to a garbage pointer, or otherwise treating it as a meaningful instance, can crash your program.
For this reason, if you aren’t going to initialize an instance reference pointer at the moment you declare it by assigning it a real value, it’s a good idea to assign it nil:
NSString* s = nil;
It’s simply a form of zero — the form of zero appropriate to an instance reference.The nil value simply means: “This instance reference isn’t pointing to any instance.”
No Overloading
It is illegal for two methods of the same type (class method or instance method) to exist in the same class with the same name but different signatures.
There are languages that permit this feature, called overloading, but Objective-C is not one of them.
The id Type
Objective-C also provides a special type designed to silence the compiler’s worries about object data types altogether. This is the id type. An id is a pointer, so you don’t say id*. It is defined to mean “an object pointer,” plain and simple, with no further specification.
Thus, every instance reference is also an id.Any object value can be assigned or typecast to an id, and vice versa.
nil is a form of zero, it's zero cast as an id.
Of course, it still makes a difference at runtime whether an id is nil or something else; sending a message to nil won’t crash the program, but sending an unknown message to an actual object will.
The compiler’s type checking is called static typing, as opposed to the dynamic behavior that takes place when the program actually runs. What I’m saying here, then, is that I prefer to take advantage of static typing as much as possible.
Messages as Data Type
@selector(myMethod:)
The term @selector() is a directive to the compiler, telling it that what’s in parentheses is a message name. Notice that what’s in parentheses is not an NSString; it’s the bare message name. And because it is the name, it must have no spaces and must include any colons that are part of the message name.So the rule is extremely easy: when a SEL is expected, you’ll usually pass a
@selector() expression.
C Functions and Struct Pointers
Moreover, many Objective-C objects and methods have lower-level C counterparts.For example, besides the Objective-C NSString, there is also something called a CFString; the “CF” stands for “Core Foundation,” which is a lower-level C-based API.A CFString is an opaque C struct (“opaque” means that the elements constituting this struct are kept secret, and that you should operate on a CFString only by means of appropriate functions). As with an NSString or any other object, in your code you’ll typically refer to a CFString by way of a C pointer; the pointer to a CFString has a type name, CFStringRef (a “reference to a CFString,” evidently). You work with a CFString in pure C, by calling functions.
You might, on occasion, actually have to work with a Core Foundation type even when a
corresponding object type exists. For example, you might find that NSString, for all its power, fails to
implement a needed piece of functionality, which is in fact available for a CFString. Luckily, an NSString
(a value typed as NSString*) and a CFString (a value typed as CFStringRef) are interchangeable: you
can use one where the other is expected, though you will have to typecast in order to quiet the worries
of the compiler. The documentation describes this interchangeability by saying that NSString and CFString are
“toll-free bridged” to one another. To illustrate, I’ll use a CFString to convert an NSString representing an integer to that integer. (This
use of CFString is unnecessary, and is just by way of demonstrating the syntax; NSString has an
intValue method.)
NSString* answer = @"42"; int ans = CFStringGetIntValue((CFStringRef)answer); The typecast prevents the compiler from complaining, and works because NSString is toll-free
bridged to CFString — in effect, behind the scenes, an NSString is a CFString.
Cocoa defines a number of underlying pointer-to-struct C data types,whose name typically ends in “Ref” (CGColorSpaceRef, CGPathRef,and so on). It is sometimes necessary to assign one of these to an id variable or parameter. For example, a CALayer’s setContents: method expects an id parameter, but the actual value must be a CGImageRef. This is legal, because a pointer is just a pointer, but the compiler will complain unless you also typecast to an id or a pointer-to-void (void*).
You might even have reason to write your own C functions as part of a class, instead of writing a method. A C function has lower overhead than a full-fledged method; so even though it lacks the object-oriented abilities of a method, it is sometimes useful to write one, as when some utility calculation must be called rapidly and frequently. Also, once in a while you might encounter a Cocoa method or function that requires you to supply a C function as a “callback.”
An example is the NSArray method sortedArrayUsingFunction:context:. The first parameter is typed like this:
NSInteger (*)(id, id, void *)
That expression denotes, in the rather tricky C syntax used for these things, a pointer to a function that takes three parameters and returns an NSInteger. The three parameters of the function are an id, an id, and a pointer-to-void (which means any C pointer). The address operator (see Chapter 1) can be used to obtain a pointer to a C function. So to call sortedArrayUsingFunction:context: you’d need to write a C function that meets this description, and use its name, preceded by an ampersand, as the first argument.
To illustrate, I’ll write a “callback” function to sort an NSArray of NSStrings on the last character of each string. (This would be an odd thing to do, but it’s only an example!)
The NSInteger returned by the function has a special meaning: it indicates whether the
first parameter is to be considered less than, equal to, or larger than the second. I’ll obtain it by calling the NSString compare: method, which returns an NSInteger with that same meaning. Here’s the function:
NSInteger sortByLastCharacter(id string1, id string2, void* context) {
NSString* s1 = (NSString*) string1;
NSString* s2 = (NSString*) string2;
NSString* string1end = [s1 substringFromIndex:[s1 length] - 1];
NSString* string2end = [s2 substringFromIndex:[s2 length] - 1];
return [string1end compare:string2end];
}
And here’s how we’d call sortedArrayUsingFunction:context: with that function as our callback (assume that arr is an NSArray of strings):
NSArray* arr2 = [arr sortedArrayUsingFunction:&sortByLastCharacter context:NULL];
Blocks
A block is an extension to the C language, introduced in Mac OS X 10.6 and available if you’re compiling for iOS 4.0 or later. It’s a way of bundling up some code and handing off that entire bundle as an argument to a C function or Objective-C method. This is similar to what we did at the end of the preceding section, handing off a pointer to a function as an argument, but instead we’re handing off the code itself. The latter has some major advantages over the former, which I’ll discuss in a moment.
As an example, I’ll rewrite the preceding example to use a block instead of a function pointer. Instead of calling sortedArrayUsingFunction:context:, I’ll call sortedArrayUsingComparator:, which takes a block as its parameter. The block is typed like this:
NSComparisonResult (^)(id obj1, id obj2)
That’s similar to the syntax for specifying the type of a pointer to a function, but a caret character is used instead of an asterisk character. So this means a block that takes two id parameters and returns an NSComparisonResult (which is merely an NSInteger, with just the same meaning as in the previous example). We can define the block and hand it off as the argument to sortedArrayUsingComparator: all in a single move, like this:
NSArray* arr2 = [arr sortedArrayUsingComparator: ^(id obj1, id obj2) {
NSString* s1 = (NSString*) obj1;
NSString* s2 = (NSString*) obj2;
NSString* string1end = [s1 substringFromIndex:[s1 length] - 1];
NSString* string2end = [s2 substringFromIndex:[s2 length] - 1];
return [string1end compare:string2end];
}];
The syntax of the inline block definition is:
^ (id obj1, id obj2) {
First, the caret character.
Then, parentheses containing the parameters.
Finally, curly braces containing the block’s content.
Thanks to the block, as you can see, we’ve combined the definition of the callback function with its use. Of course, you might object that this means the callback isn’t reusable; if we had two calls to sortedArrayUsingComparator: using the same callback, we’d have to write out the callback in full twice. To avoid such repetition, a block can be assigned to a variable:
NSComparisonResult (^sortByLastCharacter)(id, id) = ^(id obj1, id obj2) {
NSString* s1 = (NSString*) obj1;
NSString* s2 = (NSString*) obj2;
NSString* string1end = [s1 substringFromIndex:[s1 length] - 1];
NSString* string2end = [s2 substringFromIndex:[s2 length] - 1];
return [string1end compare:string2end];
};
NSArray* arr2 = [arr sortedArrayUsingComparator: sortByLastCharacter];
NSArray* arr4 = [arr3 sortedArrayUsingComparator: sortByLastCharacter];
The return type in an inline block definition is usually omitted. If included, it follows the caret character, not in parentheses. If omitted, you may have to use typecasting in the return line to make the returned type match the expected type.
Variables in scope at the point where a block is defined keep their meaning within the block at that moment, even though the block may be executed at some later moment. (Technically, we say that a block is a closure.) It is this aspect of blocks that makes them useful for specifying functionality to be executed at some later time, or even in some other thread.
Variables in scope whose meaning is captured by the closure are protected from direct assignment from within the block, unless you deliberately turn off this protection. Thus, if code inside a block tries to assign directly to a variable whose meaning comes from outside the block, the compiler will prevent it. To turn off this protection, declare the variable using the __block qualifier. But of course if such a variable is an object reference, messages can be sent to it and the object may be mutated (because message sending is not assignment) even without the __block qualifier.
Examples in this book may or may not use blocks, depending on the system version for which the example is written. If I quote code from one of my apps that runs on a pre-4.0 version of the system, that code can’t involve blocks. If I write an example targeted purely at iOS 4.0 or later, I’ll feel free to use blocks.