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 读书笔记

第 1 章 熟悉 Objective-C 第 2 章 对象消息运行时 第 3 章 接口和 API 设计 第 4 章 协议与分类 第 5 章 内存管理 循环引用 普通的两个变量互相引用 Block 循环...
  • xsl_bj
  • xsl_bj
  • 2016年06月06日 12:27
  • 4767

编写高质量的iOS代码--Effective Objective-C 2.0 读书笔记

编写高质量的iOS代码--Effective Objective-C 2.0 读书笔记 这本书年初刷完,感觉不错,介绍了很多小点,都是平日不怎么关注的. 第1章 熟悉Objective-C...
  • uxyheaven
  • uxyheaven
  • 2014年12月26日 23:56
  • 5049

【Effective Objective-C 2.0读书笔记】第三章:接口和API设计

  • freeWayWalker
  • freeWayWalker
  • 2015年07月06日 15:23
  • 592

iOS-Effective Objective-C 2.0 读书笔记(三)

第三章的内容主要是说接口和API设计相关的注意事项。比如说我们自己写的代码需要设计以便于代码复用时,应该注意的一些问题包括哪些。简单总结  有些注意事项实际上很简单,而且很常见,我觉得并不需要太长篇幅...
  • linyousong
  • linyousong
  • 2016年04月29日 22:31
  • 413

Effective Object-C 2.0

关于Object-C的类和对象的分析先暂停两天,还有关于编译器如何将OC的代码转换成C或者C++代码的部分没有做,那些对于理解OC的类和对象,消息传递等等会有不少的用途。这两天在着手翻译 Effect...
  • lipeng08
  • lipeng08
  • 2014年03月28日 15:22
  • 1551

阅读《Effective Objective-C 2.0 编写高质量iOS与OS X代码的52个有效方法》总结

第1条:了解Objective-C语言的起源 Objective-C为C语言添加了面向对象特性,是其超集。Objective-C使用动态绑定的消息结构,也就是说,在运行时才会检查对象类型。接收一...
  • caojengineer
  • caojengineer
  • 2015年06月07日 22:52
  • 1182

《Effective Objective-C 2.0编写高质量iOS与OS X代码的52个有效方法》读书笔记(下)

1.为避免在不经意间使用了无效对象,一般在release之后会清空指针,=nil; 2.通常利用弱引用或者“手动”解除引用的方式破坏循环引用。 3.ARC下,规定以alloc、new、copy、mut...
  • zhaochen_009
  • zhaochen_009
  • 2017年04月06日 10:44
  • 325

《Effective Objective-C 2.0 编写高质量iOS与OS X代码的52个有效方法》--读书笔记

个人觉得这本书很不错!里面有很多很实用但以前没有太注意的地方,所以想纪录下来,当作自己的读书笔记吧。 熟悉Objective-C 1)Objective-C语言使用“消息结构”而不是“函数调用”。O...
  • weimeng809
  • weimeng809
  • 2016年01月11日 00:18
  • 585

《Effective Objective-C 2.0编写高质量iOS与OS X代码的52个有效方法》读书笔记(上)

1.OC对象所占内存总是分配在"堆"中,而绝不会分配在"栈"中,不能再栈中分配OC对象。"栈"中对象借助栈帧进行维护,"堆"中对象的管理借助引用计数机制. -(NSMutableArray *)te...
  • zhaochen_009
  • zhaochen_009
  • 2017年04月05日 10:10
  • 290

Effective Objective-C 2.0: Object Equality

这篇挺有用的,指出了很多注意点;以后用到Object Equality可以参考 Item 8: Understand Object Equality Being able to c...
  • chuanyituoku
  • chuanyituoku
  • 2013年12月03日 11:57
  • 831
您举报文章:Effective Objective-C 2.0:Item 14 Class Object