macdevcenter.com
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Inside the Objective-C Runtime, Part Two
Pages: 1, 2

Objc_class is defined, surprisingly enough, in objc/objc-class.h. We're going to use it to extract info from the runtime. Let's look at its parts.

struct objc_class {
    struct objc_class *isa;	
    struct objc_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;
};

The first and second elements are pointers to objc_class structs. The first is the isa pointer for the class. Class objects are full-fledged objects: they each have an isa pointer to their Class. The way it works is this: an object's isa points to the Class. That Class (struct objc_class) contains all the instance variables (objc_ivar_list) declared in the Class, but its objc_method_list contains only the instance methods defined with the class.



The Class pointed to by the Class' isa (the Class' Class) contains the Class' class methods in its objc_method_list. Got that? The terminology used in the runtime is that while an object's isa points to its Class, a Class's isa points to the object's "meta Class".

So what about a meta Class' isa pointer? Well, that points to the root class of the hierarchy (NSObject in most cases). (In the Foundation framework each meta class of a subclass of NSObject "isa" instance of NSObject.) And yes, by the way, NSObject's meta Class' isa points back to the same struct -- it's a circular reference so no Class' isa is ever NULL.

Whew! The relevant bit out of all that: the object's Class has the instance methods, the class's Class (a.k.a., meta Class) has the class methods.

The next part of the struct is the super_class, a pointer to a class' superclass: the class it inherits from. The super_class pointer is NULL for root classes in the hierarchy (e.g., NSObject).

The const char *name is, big surprise, the name of the class.

The long version records information about which version of the compiler a class was compiled with. Itís checked in the runtime to see if certain features are available for a given class. We can ignore this.

Comment on this articleThoughts, feedback, comments about the content of the article? Let us know.
Post your comments

Long info contains information about the class structure: whether it's a meta class, whether it's posing as another class, etc. Again this is of use mostly to the internal functioning of the runtime and we'll ignore it. The objc/objc-class.h file contains a list of #defined values (just after the struct objc_class definition) if you're interested.

The long instance_size is the number of bytes occupied by an instance of this class.

Each of a class' instance variables (ivars) is represented by a struct objc_ivar which contains the name (ivar_name), encoded type information (ivar_type) and the ivar's offset from the instance's address in memory. You can access the structs for all of a Class' ivars by traversing its struct objc_ivar_list *ivars.

OK, enough abstract background, let's use this. Let's take a look at some of this rich trough of information. Here's how to show the ivars:

void showIvars(Class klass) {
  int i;
  Ivar rtIvar;
  struct objc_ivar_list* ivarList = klass->ivars;
  if (ivarList!= NULL && (ivarList->ivar_count>0)) {
    printf ("  Instance Variabes:\n");
    for ( i = 0; i < ivarList->ivar_count; ++i ) {
        rtIvar = (ivarList->ivar_list + i);
        printf ("    name: '%s'  encodedType: '%s'  offset: %d\n",
                rtIvar->ivar_name, rtIvar->ivar_type, rtIvar->ivar_offset);
    }
  }
}

RuntimeBrowser

At a number of places in this article I refer to the "RuntimeBrowser" (or RB). It's an application like the JavaBrowser but for Objective-C. It makes use of the information shared in this article. The source code is released under the GNU Public License, so you can download it and run it yourself. In addition to being a useful development tool in its own right, the RB provides another way to learn more about the intricacies of the ObjC runtime, including how to decode the type encodings. Enjoy.

But be warned: you'll get a lot of information. Better to display this amount of detail for individual classes. Note this code displays type information as it is encoded in the runtime. The RuntimeBrowser (see sidebar) decodes this information back to the more familiar .h file form.

In a similar way you can get information about all the methods implemented by a class. Remember a class holds the instance methods; you have use the "meta class" (klass' isa) to get information about class methods. Here's a function to display method information:

void showMethodGroups(Class klass, char mType) {
  void *iterator = 0;     // Method list (category) iterator
  struct objc_method_list* mlist;
  Method currMethod;
  int  j;
  while ( mlist = class_nextMethodList( klass, &iterator ) ) {
    printf ("  Methods:\n");
    for ( j = 0; j < mlist->method_count; ++j ) {
        currMethod = (mlist->method_list + j);
        printf ("    method: '%c%s'  encodedReturnTypeAndArguments: '%s'\n", mType,
                (const char *)currMethod->method_name, currMethod->method_types);
    }
  }
}

Again, this produces a lot of information and is best used to view details of a single class.

The remaining elements of struct objc_class include:

  • The cache, which is used to speed up message dispatch. The first time a method is invoked on a class the result of the lookup is stored in the cache -- subsequent lookups are FAST.
  • The list of protocols this class conforms to. If you're curious about displaying Protocol names, see the RuntimeBrowser.

Notice in showMethodGroups() there's a nested loop. What's going on here? Well the inner loop is of methods. And the outer loop is over groups of methods or ... categories. So now, with a little more knowledge of method lookup, we can answer the question from the first article: if you've got two methods with the same name defined on a class (at least one therefore in a category) which one gets invoked?

During method lookup, the runtime traverses the arrays of methods in the order they are stored. That order is the reverse of the order in which each group of methods is loaded. As the class must be loaded before categories are added to the class, the methods declared in a class itself are always loaded first, so end up last in the list. Since the runtime does method lookups from first-to-last, a category method will always override a method declared in the class itself.

If two different Categories define the same method, however, then it all depends on the order in which the bundles that contain those categories are loaded when an application is launched. To a large extent you can control this order by forcing bundles to be loaded when a program first starts, and being wary of loading bundles dynamically. But generally the rule is: don't redefine category methods in another category as this can lead to inconsistent results...

Please let me, or the good folks at O'Reilly, know what you think about this article. If you've noticed errors or thought of ways in which the presentation could be improved, feel free to comment in the TalkBack section at the bottom of this page.

Ezra Epstein is a long time Objective-C programmer who enjoys working on Mac OS X.


Return to MacDevCenter.com.