Effective Objective-C 2.0:Item 14 Class Object

原创 2013年12月06日 00:10:51

Class hierarchy 这个给我理清楚了 哈哈

Item 14: Understand What a Class Object Is

Objective-C is extremely dynamic in nature. Item 11 explains how the implementation for a given method call is looked up at runtime, and Item 12 explains how forwarding works when a class does not immediately respond to a certain selector. But what about the receiver of a message: the object itself? How does the runtime know what type an object is? The type of an object is not bound at compile time but rather is looked up at runtime. Moreover, a special type, id, can be used to denote any Objective-C object type. In general, though, you specify a type whenever possible so that the compiler can warn about sending messages that it thinks the receiver doesn’t understand. Conversely, any object that is of type id will be assumed to respond to all messages.

As you’ll know from Item 12, though, the compiler cannot actually know all the selectors a certain type understands, since they can be dynamically inserted at runtime. However, even if this technique is used, the compiler expects to see the method prototype defined in a header somewhere such that it can know the full method signature to be able to emit the correct code to perform the message dispatch.

Inspecting the type of an object at runtime is known as introspection and is a powerful and useful feature baked into the Foundation framework as part of theNSObject protocol, to which all objects that inherit from the common root classes (NSObject and NSProxy) conform. Using these methods rather than directly comparing classes of objects is prudent, as I will explain. However, before looking at introspection techniques, here is some background as to what an Objective-C object is.

Every Objective-C object instance is a pointer to a blob of memory. That’s why you see the * next to the type when declaring a variable:

NSString *pointerVariable = @"Some string";

If you’ve come from a C world of programming, you’ll understand exactly what this means. For the non-C programmers among you, this means that pointerVariable is a variable holding a memory address, where the data stored at that memory address is the NSString itself. The variable therefore “points to” the NSString instance. This is how all Objective-C objects are referred to; if you tried to allocate the memory for an object on the stack instead, you would receive an error from the compiler:

NSString stackVariable = @"Some string";
// error: interface type cannot be statically allocated

The generic object type, id, is already a pointer in itself, so you use it like so:

id genericTypedString = @"Some string";

This definition is semantically the same as if the variable were of type NSString*. The only difference with specifying the type fully is that the compiler can help and warn if you attempt to call a method that doesn’t exist for instances of that class.

The data structure behind every object is defined in the runtime headers along with the definition of the id type itself:

typedef struct objc_object {
    Class isa;
} *id;

Therefore, each object contains as its first member a variable of type Class. This variable defines the class of the object and is often referred to as the “is a” pointer. For example, the object “is a” NSString. The Class object is also defined in the runtime headers:

typedef struct objc_class *Class;
struct objc_class {
    Class isa;
    Class super_class;
    const char *name;
    long version;
    long info;
    long instance_size;
    struct objc_ivar_list *ivars;
    struct objc_method_list **methodLists;
    struct objc_cache *cache;
    struct objc_protocol_list *protocols;

This structure holds metadata about the class, such as what methods instances of the class implement and what instance variables instances have. The fact that this structure also has an isa pointer as its first variable means that a Class is itself an Objective-C object. This structure also has another variable, called super_class,which defines the class’s parent. The type of a class (i.e., the class the isa pointer points to) is another class, known as the metaclass, which is used to describe the metadata about instances of the class itself. This is where class methods are defined, since they can be thought of as instance methods of instances of a class. There is only ever one instance of a class, though, and only one instance of its associated metaclass.

The hierarchy of a class called SomeClass that inherits from NSObject looks like the one shown in Figure 2.6.


Figure 2.6 Class hierarchy for instances of SomeClass, which inherits fromNSObject, including the metaclass hierarchy

The super_class pointer creates the hierarchy, and the isa pointer describes the type of an instance. You can manipulate this layout to perform introspection. You can find out whether an object responds to a certain selector and conforms to a certain protocol and determine information about what part of the class hierarchy the object belongs to.

Inspecting the Class Hierarchy

The introspection methods can be used to inspect the class hierarchy. You can check whether an object is an instance of a certain class by using isMemberOfClass: orwhether an object is an instance of a certain class or any class that inherits from it by using isKindOfClass:. For example:

NSMutableDictionary *dict = [NSMutableDictionary new];
[dict isMemberOfClass:[NSDictionary class]]; ///< NO
[dict isMemberOfClass:[NSMutableDictionary class]]; ///< YES
[dict isKindOfClass:[NSDictionary class]]; ///< YES
[dict isKindOfClass:[NSArray class]]; ///< NO

This kind of introspection works by using the isa pointer to obtain the object’s class and then walking the inheritance hierarchy, using the super_class pointer. Given the dynamic nature of objects, this feature is extremely important. You cannot ever fully know the type without introspection, unlike other languages with which you might be familiar.

Introspection of the class of an object is extremely useful given the dynamic typing used by Objective-C. Introspection is commonly used when retrieving objects from collections, since they are not strongly typed, meaning that when objects are retrieved from collections, they are usually of type id. Introspection can then be used if the type needs to be known: for example, when needing to generate a comma-separated string from objects stored in an array to be saved to a text file. The following code could be used in that scenario:

- (NSString*)commaSeparatedStringFromObjects:(NSArray*)array {
    NSMutableString *string = [NSMutableString new];
    for (id object in array) {
        if ([object isKindOfClass:[NSString class]]) {
            [string appendFormat:@"%@,", object];
        } else if ([object isKindOfClass:[NSNumber class]]) {
            [string appendFormat:@"%d,", [object intValue]];
        } else if ([object isKindOfClass:[NSData class]]) {
            NSString *base64Encoded = /* base64 encoded data */;
            [string appendFormat:@"%@,", base64Encoded];
        } else {
            // Type not supported
    return string;

It is also possible to check class objects for equality. You can do so by using the ==operator rather than an isEqual: method, as you would usually use when comparing Objective-C objects (see Item 8). The reason is that classes are singletons, and so only a single instance of each class’s Class object is within an application. Thus, another way to test whether an object is exactly an instance of a class would be to do the following:

id object = /* ... */;
if ([object class] == [EOCSomeClass class]) {
    // 'object' is an instance of EOCSomeClass

However, you should always prefer the introspection methods to direct equality of class object, because the introspection methods are able to take into account objects that make use of message forwarding (see Item 12). Consider an object that forwards all selectors to another. Such an object is called a proxy, and NSProxy is a root class specifically for objects like that.

Usually, such proxy objects will return the proxy class (i.e., subclass of NSProxy) if the method class is called rather than the class of the object being proxied. However, if introspection methods, such as isKindOfClass:, are called on them, they will proxy the message through to the proxied object. This means that the answer to the message will be as though the proxied object is being inspected. Therefore, this will yield a result different from inspecting the class object returned from calling the class method, since that will be the proxy class itself rather than the class of the proxied object.

Things to Remember

Image The class hierarchy is modeled through Class objects that each instance has a pointer to in order to define its type.

Image Introspection should be used when the type of the object cannot be known for certain at compile time.

Image Always prefer introspection methods where possible, rather than direct comparison of class objects, since the object may implement message forwarding.


Effective Objective-C 2.0: Item 43: Know When to Use GCD and When to Use Operation Queues

Item 43: Know When to Use GCD and When to Use Operation Queues GCD is a fantastic technology, but...

Effective Objective-C 2.0: Item 16:Designated Initializer

Item 16: Have a Designated Initializer All objects need to be initialized. When initializing an o...

Effective Objective-C 2.0:Item 25: Always Prefix Category Names on Third-Party Classes

Item 25: Always Prefix Category Names on Third-Party Classes Categories are commonly used to add ...

Effective Objective-C 2.0: Item 41: Prefer Dispatch Queues to Locks for Synchronization

Item 41: Prefer Dispatch Queues to Locks for Synchronization Sometimes in Objective-C, you will com...

Effective Objective-C 2.0:Item 20: Prefix Private Method Names

Item 20: Prefix Private Method Names It’s extremely common for a class to do much more than it ap...

Effective Objective-C 2.0: Item 12: Understand Message Forwarding

这点很有意思,理清楚了Message Forward的过程(特别那个例子有意思),但是感觉 具体例子太少了~~ 难道是我没有理解透? Item 12: Understand Messag...

Effective Objective-C 2.0: Item 39: Use Handler Blocks to Reduce Code Separation

Item 39: Use Handler Blocks to Reduce Code Separation A common paradigm in programming a user int...

Effective Objective-C 2.0:Item 7: Access Instance Variables

initializer: set 时候直接访问ivar;get时候看具体; 一般情况下,set用accessors; get用ivar快  Item 7: Access Instanc...

Effective Objective-C 2.0: Item 35: Use Zombies to Help Debug Memory-Management Problems

Item 35: Use Zombies to Help Debug Memory-Management Problems Debugging memory-management issues ...

Effective Objective-C 2.0: Item 37: Understand Blocks

Effective Objective-C 2.0 介绍Blocks的文章,有些论点比较新颖,觉得不错,值得一看...
您举报文章:Effective Objective-C 2.0:Item 14 Class Object