Objective-C for C++ Programmers

Objective-C for C++ Programmers, Part 1
 from www.informit.com
 by David Chisnall
 nov 21,2008

David Chisnall has been working with Objective-C for some years, both as a user
and a compiler writer. In this three-part series, he gives a guided tour of the
language. Part 1 takes a look at the history of the language and its underlying
philosophy.


Regular readers will know that I spend a lot of my time these days writing
Objective-C code. Now that I've finished writing a Smalltalk compiler that
generates code where the objects are toll-free bridged with Objective-C objects,
that situation might change. In the meantime, however, this article will provide
an introduction to Objective-C for people who are more familiar with C++.

Objective-C was designed as a minimal set of extensions to C providing
Smalltalk-like semantics. It also included Smalltalk-like syntax, which makes it
easy to see which bits of code are pure C, and which bits contain the
Objective-C extensions.

Unlike C++, Objective-C is a pure superset of C. Every C program is a valid
Objective-C program. All of the new keywords in Objective-C are prefixed by the
@ character, which is not valid in C identifiers.

The Runtime System
The first implementation of Objective-C was a preprocessor, which turned
Objective-C into C. The dynamic features of the language were all supported by a
runtime library. Modern compilers don't use C as an intermediate language, but
they still have the same arrangement with a runtime library providing dynamic
features.

In Objective-C, objects are just C structures with the first element being a
pointer to another C structure representing the class. Exactly what this class
structure contains varies a little bit between runtimes, but at the very least
it contains the information required to look up methods, the layout of the
instance variables ("member fields," in C++ speak) and some other metadata.

Since Objective-C is a pure superset of C, and all of the features of the
runtime library (including the structure of classes) are exposed to C, this
means that Objective-C fully exposes the object model to the programmer. There's
no "magic" in Objective-C. When you call a method in C++, the compiler does some
complicated things with vtables to find the correct code to run. In Objective-C,
it does one of the following:

Calls a C function to look up the function that implements the method (with the
GNU runtime)
Calls a C function that looks up the method and calls it in one operation (with
the Apple runtime).
This means that you can write pure C code that interacts with Objective-C
objects. It also allows you to bypass some of the dynamic behavior at times when
it's not needed.

Some History
Objective-C was created in the early 1980s by Brad Cox as a set of minimal
extensions to C to support Smalltalk-style objects easily. It grew out of the
Object Oriented Pre-Compiler (OOPC) project, which had very similar semantics to
those of Objective-C but some really horrible syntax.

The initial implementation and the Objective-C trademark were bought by NeXT,
which used it all over its new operating system. NeXT built a GUI toolkit in
Objective-C and an interface builder. This is widely considered to be the first
commercial rapid application development (RAD) tool. It also had the nice
side-effect that the interface builder didn't just store an interface
description—it stored serialized objects. This approach allowed a clean
model-view-controller separation to be maintained while storing the controller
and view in a loadable format.

In the early 1990s, NeXT and Sun partnered to build a portable version of the
NeXT environment. While NeXT's initial system used pure C in a lot of places,
the new version defined two frameworks: Foundation and AppKit. Foundation
defined core functionality, including collection classes, strings, and a variety
of other things. AppKit contained the GUI frameworks. The two were defined in
the OpenStep specification, which was implemented by NeXT, Sun, and later by the
GNU Project. After buying NeXT, Apple renamed its OpenStep implementation
"Cocoa." Since OpenStep was based on earlier NeXT designs, all of the classes in
OpenStep are prefixed with NS.

The story of GCC's support for Objective-C is somewhat interesting. The initial
implementation was written by NeXT, but kept in a separate library that users
would link with their version of GCC. By not distributing it with GCC, NeXT
hoped to circumvent the GPL. This trick didn't work, and NeXT was eventually
required to release the code. However, releasing the source code for the runtime
library wasn't required, which meant that the compiler wasn't useful on any
other platforms. Richard Stallman wrote a new version that was a drop-in
replacement for the NeXT runtime. This was later replaced by newer versions that
diverged in various ways.

The current code for Objective-C in GCC depends on the branch you use. NeXT kept
its own version, which has a barely-maintainable mess with no layering and
everything in a single source file, and only supports the NeXT runtime. Mainline
GCC includes a completely unmaintainable mess, based on the NeXT code but
littered with #ifdefs for the GNU runtime.

Once Objective-C was supported by GCC, attempts to replicate the NeXT
environment started. The GNUstep project eventually evolved from these, and
implements pretty much all of OpenStep and a number of Cocoa additions. It also
includes a replacement for NeXT's Interface Builder: the GNU Object Relational
Modeler (GORM).

Some Syntax
The syntactic additions in Objective-C were designed to highlight the various
semantics of the object-oriented code. In C++, you can do this:

doSomething();This can be calling a C function, calling a static C++ member
function with the current object passed as a hidden parameter, or calling a
virtual function, which might be implemented in a subclass, with the current
object as a hidden parameter. Alternatively, you might do this:

a.doSomething();This could be calling a C function pointer that's a field of the
a structure, calling a static member function defined on the class of which you
think a is an instance, or a virtual function defined on the class of which a
really is an instance.

In contrast, things that look like C in Objective-C always are C. The equivalent
for calling a method in Objective-C comes from the Smalltalk syntax:

[a doSomething];The square brackets are used to wrap some Smalltalk-like
message-sending syntax. I'll come back to the difference between sending
messages and calling functions a bit later. When you have a method that takes
some arguments, the arguments all have names in Objective-C, like this:

[aDictionary setObject:@"a string" forKey:@"a key"];This might seem strange to
someone who is more familiar with C syntax, but it means that you spend much
less time thinking about the order of parameters. It also makes the code much
easier to read if you haven't previously come across the class of which
aDictionary is an instance—you know that this method is setting an object and a
key, and you know which is which.

Under the hood, this is turned into something like the following:

SEL sel = sel_get_uid("setObject:forKey:");
IMP method = objc_msg_lookup(aDictionary->isa, sel);
method(aDictionary, sel, @"a string", @"a key");I'll come back to exactly what
sel is a bit later. While this will work, it isn't quite what happens—the value
of sel will be filled in by the runtime library's initialization routine when
the module is loaded. The NeXT/Apple family of runtimes combine the last two
steps into something like this:

objc_msgSend(aDictionary, sel, @"a string", @"a key");This is slightly faster,
but makes implementing optimizations such as speculative inlining in the future
a lot harder. Because these are C functions, nothing about them is special—you
can call them from your own code, if you want. One of the fun things in the LLVM
C language family front end (clang) tree is a rewriter that takes an Objective-C
source file and compiles it to a C source file compatible with the NeXT runtime
library.

The Object Model
The term object oriented was coined by Alan Kay in the 1960s. He went on to
develop the Smalltalk language with a team at Xerox PARC as an embodiment of the
ideas in this programming model.

The basic idea behind object-oriented programming is to have simple models of
computers that communicate by exchanging messages. This is very different from
the C++ approach, which has something a bit like object-orientation, but based
on the idea of associating functions with data structures.

In C++, objects are structures with a set of associated functions that are
either static or virtual. A static function is semantically equivalent to a C
function with a hidden first argument containing the object. This is a
completely pointless extension to C, since the following two are equivalent:

// C++
Object->function();

// C
function(Object);The C++ version is more to type, but doesn't give any semantic
differences.

C++ also supports something slightly more clever in the form of virtual member
functions. If function in the previous example is a virtual function, which real
function is called depends on the class of Object. If it's static, which
function is called depends on what the caller thinks the class of Object is.

In Objective-C, no equivalent of a static member function was added. If you want
this functionality, you just use C functions. Methods in Objective-C are similar
to C++ virtual member functions, but with a few important differences. The first
is that the syntax is different:

[object message];This sends the message named message to the object. Which code
is executed as a result of this action depends entirely on the class of the
object. There is one very important difference between this and the C++
equivalent: In C++, this mechanism still depends slightly on what you think the
class of the object is.

Consider that you have a class hierarchy with four classes. A is a root class, B
and C are subclasses of A, and D is a subclass of B. If all of these except A
implement a virtual doSomething() function in C++, you can only call this using
a template. Consider the following line:

Object.doSomething();If you think Object is of class B or D, and it really is D,
the implementation declared by D is called. If you think it's of class C, it
will call the implementation in C. If you think it's an instance of A, you need
to try two explicit dynamic casts and see which one works, or use a template.

If you have the same class arrangement in Objective-C, with B, C, and D
implementing a doSomething method, you could try this:

[object doSomething];If you think object is of type B, but it's really type C,
the doSomething method will still be called. If you think it's an instance of
class A, you'll get a compile-time warning that instances of A don't respond to
a doSomething message. If it's really an instance of B, C, or D, it will just
work at runtime, but if it's really an instance of A, you'll get a runtime
exception here.

Multiple inheritance is not available in Objective-C, but is far less needed
than in C++. In C++, method lookup is based on the inheritance chain, so you
can't use two classes interchangeably unless they have a common superclass in
C++. In Objective-C, you can use any two classes entirely interchangeably if
they respond to the same messages.

Summary
We've looked at the history of Objective-C and the core ideas behind the
language. We've also discovered the most important part of the language: sending
messages to objects. In part 2, we'll take a look at more of the core syntax.

//******************************************************************************
**
Part 2 of David Chisnall's three-part series on Objective-C discusses the core
syntax of the language.
Part 1 of this series examined the history and philosophy of Objective-C. This
article starts investigating some of the concrete syntax. As you might expect,
this involves defining and using classes.

Classes Aren't Special
In Smalltalk, classes are just objects with a few special features. The same is
true in Objective-C. A class is an object. It responds to messages just as an
object does. Both Objective-C and C++ split object allocation and
initialization:

In C++, object allocation is done via the new operator. In Objective-C, it's
done by sending the class an alloc message—which, in turn, calls malloc() or an
equivalent.
Initialization in C++ is done by calling a function with the same name as the
class. Objective-C doesn't distinguish between initialization methods and other
methods, but by convention the default initialization method is init.
When you declare a method to which instances respond, the declaration starts
with -, and + is used for class methods. It's common to use these prefixes for
messages in documentation, so you would say +alloc and -init, to indicate that
alloc is sent to a class and then init is sent to an instance.

Classes in Objective-C, as in other object-oriented languages, are object
factories. Most classes don't implement +alloc themselves; instead, they inherit
it from the superclass. In NSObject, the root class in most Objective-C
programs, the +alloc method calls +allocWithZone:. This takes an NSZone as an
argument, a C structure containing some policy for object allocations. Back in
the 1980s, when Objective-C was used in NeXTSTEP to implement device drivers and
pretty much all of the GUI on machines with 8MB of RAM and 25 MHz CPUs, NSZone
was very important for optimization. At the moment, it's more or less completely
ignored by Objective-C programmers. (It has potential to become more relevant as
NUMA architectures become more common, however.)

One of the nice features of the fact that object creation semantics are defined
by the library and not the language is the idea of a class cluster. When you
send an -init message to an object, it returns an initialized object. This may
be the object to which you sent the message (and usually is), but it doesn't
have to be. The same is true of other initializers. It's possible to have
specialized subclasses of a public class that are more efficient on different
data.

One common trick for implementing this feature is called isa-swizzling. As I
said earlier, Objective-C objects are C structures whose first element is a
pointer to the class. This element is accessible, just as any other instance
variables are; you can change the class of an object at runtime simply by
assigning a new value. Of course, if you set the class of an object to something
that has a different layout in memory, things will go horribly wrong. However,
you can have a superclass that defines the layout and then a set of subclasses
that define the behavior; for example, this technique is used by the standard
string class (NSString), which has various instances for different width text
encodings, for static strings, and so on.

Because classes are objects, you can do pretty much anything with them that you
would with objects. For example, you can put them in collections. I use this
format fairly often when I have a set of incoming events that need handling by
instances of different classes. You could create a dictionary mapping the event
names to the classes, and then instantiate a new object for each incoming event.
If you do this in a library, it allows users of the code to register their own
handlers easily.

Types and Pointers
Objective-C doesn't officially allow defining objects on the stack. Well, this
isn't quite true—it's possible to define objects on the stack, but it's very
difficult to do correctly because it breaks assumptions about how memory
management works. As a result, every Objective-C object is a pointer. A few
types are defined by Objective-C; these are defined in a header as C types
(remember the "no magic" thing).

The three most commonly used new types in Objective-C are id, Class, and SEL. An
id is a pointer to an Objective-C object. It's the equivalent of a void* in C,
in that you can cast any object pointer type to it and cast it to any other
object pointer type. You can try sending any message to an id, but you'll get a
runtime exception if it isn't supported.

A Class is a pointer to an Objective-C class. Classes are objects, so they also
can receive messages. The name of a class is a type, not a variable. The
identifier NSObject is the type of an NSObject instance, but it also can be used
as a message receiver. You can get a class like this:

[NSObject class];This sends a +class message to the NSObject class, which then
returns a pointer to the Class structure representing class. This is useful for
introspection, as we'll see in part 2 of this series.

The third type, SEL, represents a selector—an abstract representation of a
method name. You can construct one at compile time with the @selector()
directive, or at runtime by calling a runtime library function with a C string
or by using the OpenStep NSSelectorFromString() function, which gives a selector
for an Objective-C string. This technique allows you to call methods by name.
You can do this in C by using something like dlsym(), but it's much more
difficult in C++. In Objective-C, you can do something like this:

[object performSelector:@selector(doSomething)];This is equivalent to the
following:

[object doSomething];Obviously, the second form will be slightly faster, since
the first does two message sends. Later on, we'll look in a bit more detail at
some of the things you can do with selectors.

C++ has no equivalent of the id type, because objects must always be typed. In
Objective-C, you have an optional type system. Both of the following are valid:

id object = @"a string";
NSString *string = @"a string";The constant string is really an instance of the
NSConstantString class, which is a subclass of NSString. Assigning it to an
NSString* enables compile-time type checking for messages and access to public
instance variables (which are almost never used in Objective-C). Note that you
can subvert this setup by doing something like the following:

NSArray *array = (NSArray*)string;If you send messages to array, the compiler
will check that they're messages that NSArray understands. This isn't very
useful, because the object is a string. If you send it messages that both
NSArray and NSString implement, however, it will work. If you send it messages
that NSString doesn't implement, an exception will be thrown.

While this may seem like a strange thing to do (and it is—so don't do it), it
highlights a very important difference between Objective-C and C++. Objective-C
has typed-value semantics, while C++ has typed-variable semantics. In
Objective-C, the type of an object is exclusively a property of the object. In
C++, the type depends on the type of the variable. When you assign a pointer to
an object in C++ to a variable defined as being a pointer to a superclass, the
two pointers may not have the same numerical value. (This is done to allow
multiple inheritance, which Objective-C doesn't support.)

Defining Classes
Objective-C class definitions have an interface and an implementation section.
C++ has something slightly similar, but mixes the two somewhat. The interface in
Objective-C only defines the bits that definitely need to be public. For
implementation reasons, this includes private instance variables in most
implementations, since you can't subclass a class unless you know how big it is.
More recent implementations, such as Apple's 64-bit runtime, don't have this
limitation.

The interface of an Objective-C object looks something like this:

@interface AnObject : NSObject <AProtocol, AnotherProtocol> {
@private
    int integerivar
@public
    id anotherObject;
}
+ (id) aClassMethod;
- (id) anInstanceMethod:(NSString*)aString with:(id)anObject
@endThe first line of this contains three parts: The identifier AnObject is the
name of the new class. The name after the colon is NSObject. (This is optional,
but pretty much every Objective-C object should extend NSObject.) The names in
angle brackets are protocols—similar to interfaces in Java—that are
implemented by this class.

As with C++, instance variables (fields, in C++) can have access qualifiers.
Unlike in C++, these qualifiers are prefixed with @ to avoid collisions with
valid C identifiers.

Objective-C doesn't support multiple inheritance, so there's only one
superclass. Therefore, the layout for the first part of an object is always
identical to the layout of instances of the superclass. This used to be defined
statically, meaning that changing the instance variables in one class required
all subclasses to be recompiled. In newer runtimes this definition isn't
required, at the cost of slightly more expensive access to instance variables.
The other side-effect of this decision is that it breaks one of the other
features of Objective-C:

struct _AnObject
{
    @defs(AnObject);
};The @defs directive means "Insert all the fields of the specified object into
this structure," so struct _AnObject has exactly the same in-memory layout as an
instance of the AnObject class. You can use this rule, for example, to access
instance variables directly. A common use is to allow a C function to manipulate
an Objective-C object directly, for performance reasons.

Another thing you can do with this feature, as I hinted earlier, is create
objects on the stack. Since the structure and the object have the same in-memory
layout, you simply create a structure, set its isa pointer to the correct class,
and then cast a pointer to it to an object pointer. Then you can use it as an
object, although you have to be very careful that nothing retains pointers to it
beyond the end of the structure's scope. (I've never found a real-world use for
this trick; it's purely as an academic exercise.)

Unlike C++, Objective-C has no private or protected methods. Any method on an
Objective-C object can be called by any other object. If you don't declare
method in the interface, it's informally private. You'll get a compile-time
warning that the object may not respond to this message, but you can still call
it.

The interface is similar to the forward declaration in C. It still needs an
implementation, which, unsurprisingly, uses @implementation to define:

@implementation AnObject
+ (id) aClassMethod
{
 ...
}
- (id) anInstanceMethod:(NSString*)aString with:(id)anObject
{
 ...
}
@endNotice how parameters types are specified, in brackets. This is reusing the
casting syntax from C to show that the values are cast to that type; they might
not actually be that type. Exactly the same rules apply here as when casting.
This means that casting between incompatible object pointer types will cause a
warning (not an error).

Memory Management
Traditionally, Objective-C didn't provide any memory management facilities. In
early versions, the root Object class implemented a +new method that called
malloc() to create a new object. When you had finished with the object, you sent
it a -free message. OpenStep added reference counting. Every object that
inherits from NSObject responds to a -retain and a -release message. When you
want to retain a pointer to an object, you send it a -retain message. When
you've finished with it, you send it a -release message.

This design has one slight problem. Often you don't want to keep a pointer to an
object, but you don't want it to be freed quite yet, either. A typical example
is when returning an object. The caller may want to keep a pointer to the
object, but you don't.

The solution to this problem was the NSAutoreleasePool class. In addition to
-retain and -release, NSObject also responds to an -autorelease message. When
you send one of these, it registers itself with the current autorelease pool.
When this pool object is destroyed, it sends a -release message to every object
that received an -autorelease message earlier. In OpenStep applications, an
NSAutoreleasePool instance is created at the start of every run loop and
destroyed at the end. You can also create your own instances to free
autoreleased objects sooner.

This mechanism eliminates a lot of the copying that C++ needs. It's also worth
noting here that, in Objective-C, mutability is an attribute of the object, not
of the reference. In C++, you have const pointers and non-const pointers. You're
not allowed to call non-const methods on a const object. This doesn't guarantee
that the object won't be changed—simply that you won't change it.

In Objective-C, a common pattern defines an immutable class and then a mutable
subclass. NSString is a typical example; it has a mutable subclass,
NSMutableString. If you receive an NSString and you want to keep it, you can
send it a -retain message and keep the pointer without a copy operation.
Alternatively, you could send NSString a +stringWithString: message. This then
checks whether the argument is mutable, and, if it isn't, returns the original
pointer.

Objective-C 2.0 from Apple and the GNU runtime both support conservative garbage
collectors, which allow you to avoid the need for -retain and -release messages.
This addition to the language isn't always well-supported by existing
frameworks, however, and should be used with care.

Summary
Now that we've seen the core of the Objective-C language, in the conclusion of
this series we'll take a look at some of the more advanced topics.

//******************************************************************************
**
Objective-C for C++ Programmers, Part 3

David Chisnall concludes his three-part series on Objective-C with an
exploration of some of the more advanced concepts in the language.
In part 1 and part 2 of this series, we looked at the core of the Objective-C
language, defining classes, creating objects, and sending messages to them. This
final article covers the more advanced parts of the language.

Introspection
C++ has some support for introspection via Runtime Type Information (RTTI). This
is generally poorly supported across compilers, though; even where it's
supported, it typically isn't used, "for performance reasons." In contrast,
introspection information is always available in Objective-C.

The class structure typically contains a linked list or array of metadata about
methods and another about instance variables. For each instance variable it
includes the offset from the start of the object, the type, and the name. You
can use this setup to do some quite interesting things. For example, I've
written a framework for étoilé that inspects this information and uses it to
serialize objects automatically (only possible in C++ for objects for which you
have the source code). For methods, it has the name, the types, and a pointer to
the function used to implement the method. This is an Instance Method Pointer
(IMP), and is defined in a header as follows:

typedef id (*IMP)(id, SEL, ...);This uses two other Objective-C types. The id
type is a pointer to some kind of object. All you know about an id is that it
will respond to messages (although you don't know which messages, unless you ask
it). The other is SEL, the type of a selector.

You can do this:

IMP method = [object methodForSelector:@selector(doSomething)];The variable
method is now a C function pointer to the implementation of the method. If
you're sending the same message to an object, you can use this capability for
some extra speed.

Since you can construct selectors at runtime, you can use this approach for some
very dynamic behavior. In the XML parser in étoilé, I use this when adding
child elements to an object encapsulating an XML element. The root class
implements an -addChild:forKey: method containing the following code:

NSString * childSelectorName = [NSString stringWithFormat:@"add%@:", aKey];
SEL childSelector = NSSelectorFromString(childSelectorName);
if([self respondsToSelector:childSelector])
{
  [self performSelector:childSelector withObject:aChild];
}This constructs a selector from the key name and then calls it with the object.
This technique is used a lot in the XMPP implementation. For example, a <name>
tag might be parsed by a class that just collects the character data and turns
it into a string. When it gets to </name>, it does something like this:

[parent addChild:string forKey:@"name"];The parent then runs this method, which
will call its -addname: method with the string as an argument. A more complex
form is found in Cocoa in the form of key-value coding, which introspects both
method and instance variable metadata. When you call -setValue:forKey: it will
call a setter, set an instance variable directly, or call
-setValue:forUndefinedKey:. Using key-value coding is slower than setting
instance variables directly or even calling set/get methods directly, but it
completely isolates the implementation from the interface.

With Objective-C 2.0, Apple introduced properties, accessed this way:

object.property = 12;These are a very thin layer of syntactic sugar. Internally,
they're translated to set and get messages depending on how the property is
used. The best feature of properties is that they allow these set and get
methods to be generated automatically for any given instance variable, with
retain, assign, or copy semantics.

Protocols
In Objective-C, protocols are sets of messages that a class implements. You can
specify that a pointer should refer to a class implementing a given interface:

id<AnInterface> object;
NSString<AnInterface> *string;The first example is the equivalent of declaring
the variable as the type of an interface in Java. In C++, the closest equivalent
is to use abstract classes instead of interfaces and specify the abstract class
as the type.

The second example is more interesting. The string variable is allowed to be any
NSString subclass that implements AnInterface, which allows you to restrict a
type to a subset of subclasses that implement a particular interface. A common
example is something like the following:

NSObject<MyDelegateProtocol> *delegate;This allows it to be any subclass of
NSObject (and so the methods you generally expect from any object will work),
and additionally requires it to implement the specified protocol. Here's an
alternative:

id <NSObject, MyDelegateProtocol> delegate;This works for NSObject, because
NSObject is both a class and a protocol, with the class adopting the protocol.
This was done so NSProxy and other root classes can be used interchangeably with
NSObject subclasses.

Because Objective-C comes from Smalltalk, with the "everything is an object"
mentality, it shouldn't be a surprise that protocols, like classes, are also
objects. As such, you can send them messages to perform introspection.
Typically, you don't do this yourself, instead relying on messages sent to
NSObject subclasses:

if ([object conformsToProtocol:@protocol(MyDelegateProtocol)])
{
  // Do something with the delegate
}One thing that's slightly confusing about protocols is the fact that they're
tested for equality simply by comparing their names. If you have two protocols
with the same name, you have no way of telling, at runtime, which one an object
implements. The reasoning was to allow testing for conformance using the
@protocol() directive as shown above in source files that don't have access to
the protocol description, but it can cause confusion.

Extending Classes
One things in Objective-C that has no direct analog in C++ is the idea of a
category—a collection of methods that are added to an existing class. Once the
category is loaded, they have no difference from other methods. Categories, like
classes, are declared with an interface and an implementation.

Category interfaces can be used without a corresponding implementation to expose
other methods, giving something like a friend class in C++. If you implement
methods in a class, but don't declare them in an interface, you'll get a
compile-time warning when you use them. You can create a category interface like
this:

@interface AnObject (Private)
- (void) privateMethod;
@endIf you put this at the top of your implementation file, you won't get a
compile-time warning when you send a -privateMethod message to instances of
AnObject. The name in parentheses (Private) is just the name of the category.
This can be anything. Note that, without an @implementation section, this is
just like a C forward declaration of a function. If there's no corresponding
function implementation in C, you'll get a linker error. If there's no
corresponding method implementation in Objective-C, you'll get a runtime
exception.

You can provide extra method implementations using a category in exactly the
same way as when you provide "normal" methods:

@implementation AnObject (NewMethods)
- (void) newMethod
{
  ...
}
@endIf you send a -newMethod message to any instance of AnObject, this method
will be called. You can also use this to replace existing methods in an object
with your own version, so you don't need access to the source code for the
object in question. This approach is often used to instrument various methods in
library classes, but can also be used to patch bugs in third-party libraries for
which you don't have the source code.

One of the less-documented parts of categories is that categories allow you to
add conformance to protocols to existing objects. If your category adopts a
protocol, you'll get a compile-time warning if you don't provide implementations
for every method, and will be able to do runtime tests to check for conformance.
We use this facility in étoilé to add conformance to a collection protocol to
all of the collection classes in Foundation, giving them a consistent interface.

Informal Protocols
A fairly common pattern in Objective-C is the idea of an informal protocol,
which is a collection of methods that a class may or may not implement. A common
use is for delegate objects. In Java, it's very common for delegates to be
expected to implement an interface. This approach often has a number of methods
where several are implemented as methods with no body, which is needlessly
verbose.

There are two ways of defining informal protocols in Objective-C. The first way
is by defining a category on a base class (typically NSObject) that provides a
null implementation of each method. This means that every class will respond to
the messages in the interface, but only the ones that explicitly implement the
method will do anything. You would put something like this in the source file
that used the informal protocol:

@implementation NSObject (MyInformalProtocol)
- (void) handleSomethingFrom:(id) sender {}
@endThen you simply send a handleSomethingFrom: message to the delegate object.
Note that you don't need an @interface section to provide separation of
interface and implementation for categories; the interface is private, and you
don't want anything other than your class to be calling this method, so you
don't publish an interface. While this technique is simple, it's not ideal in
many respects, because it involves filling the root object's dispatch table with
mostly-unused cruft (which can cause a slight performance degradation on the
Apple runtime due to how it implements caching).

Another option is to perform runtime testing. If you send an object a
respondsToSelector: message, you can find out whether it implements a named
method. For delegates, you can then cache the IMP for the method and call this
directly in the future. In your setDelegate: method, you would do something like
this:

handleMethod = NULL;
if([delegate respondsToSelector:@selector(handleSomethingFrom:))
{
  handleMethod = [delegate methodForSelector: @selector(handleSomethingFrom:)];
}Then, where this is used, you would do the following:

// Equivalent to [delegate handleSomethingFrom:self];
if (NULL != handleMethod
{
  handleMethod(delegate, @selector(handleSomethingFrom:), self);
}Objective-C 2.0 offers a third option, which is to use an @optional directive
in a protocol declaration. This is largely a waste of space, since you still
need to perform a runtime test to determine whether an object that conforms to a
protocol actually does implement an optional method from that protocol.

Second-Chance Dispatch
In C++, you don't send messages, you call member functions. This is an important
distinction, because it means that calling methods is semantically similar to a
function call. Objective-C adds another layer of abstraction. If you send a
message to an Objective-C object that it doesn't understand, an exception will
be thrown. This isn't done by the language, however.

The runtime library has a fallback mechanism when no method is found for a
selector. It calls a method that introspects the receiver for some type
information, wraps up the call in an NSInvocation object, and then passes this
to the object's -forwardInvocation: method.

The NSInvocation object encapsulates the receiver, the selector, and the
arguments. You can use this idea for things like higher-order messaging.
Consider the following example usage:

[[anArray map] toUppercase];The -map method on the array returns a proxy object
with a forwardInvocation: method implemented like this:

- (void) forwardInvocation:(NSInvocation*)anInvocation
{
 SEL selector = [anInvocation selector];
 NSMutableArray * mappedArray = [NSMutableArray array];
 FOREACHI(array, object)
 {
  if([object respondsToSelector:selector])
  {
   [anInvocation invokeWithTarget:object];
   id mapped;
   [anInvocation getReturnValue:&mapped];
   [mappedArray addObject:mapped];
  }
 }
 [anInvocation setReturnValue:mappedArray];
}The FOREACHI macro is from étoilé, and simply performs some IMP caching on an
NSEnumerator. When you send the -toUppercase message to the map proxy, it
iterates over every object in the array; checks whether it responds to the
selector; and, if it does, calls the method with the arguments. The return value
is then added to a new array.

This is pretty much impossible to do in C++. You can use the command pattern to
do something similar, but only by implementing your own dispatch mechanism on
top of C++.

Summary
Objective-C is a very small language, and we've covered almost all of it in this
series, including some quite advanced features. As with Smalltalk, however, this
is somewhat deceptive. While the core of the Objective-C language is very
simple, a lot of the behavior of an Objective-C program comes from the library.
This is usually an implementation of the OpenStep specification, such as GNUstep
or Apple's Cocoa, and familiarity with the library can take a long time to
achieve.

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值