Monday, February 13, 2006

Nil and nil

Objective-C has some very interesting data types that often are misunderstood. Many of them can be found in /usr/include/objc/objc.h, or other files in that same directory. Below is a snippet taken from objc.h that shows the declaration of some of these types:


// objc.h
#import <objc/objc-api.h>

typedef struct objc_class *Class;

typedef struct objc_object {
Class isa;
} *id;

typedef struct objc_selector *SEL;
typedef id (*IMP)(id, SEL, ...);
typedef signed char BOOL;

#define YES (BOOL)1
#define NO (BOOL)0

#ifndef Nil
#define Nil 0 /* id of Nil class */
#endif

#ifndef nil
#define nil 0 /* id of Nil instance */
#endif


Let's cover some of them in a little more detail here:



id

This is not equivalent to void *. As the snippet from the header above indicates, id is a pointer to a struct objc_object, which is basically a pointer to any class derived from the Object (or NSObject) base class. Notice, that id is a pointer, so you do not need the asterisk when using id. For example: id foo = nil declares a nil pointer to any subclass of NSObject, whereas id *foo = nil declares a pointer to a pointer to a subclass of NSObject.


nil

This is equivalent to the C language's NULL value. It is defined in objc/objc.h and is used to refer to an Objective-C object instance pointer that points to nothing.


Nil

Yes, this is sort-of different than nil but they're defined in the same file. Nil (with a capital 'N') is used to define a pointer to an Objective-C class (type Class) that points to nothing.


SEL

Now this one is fun and interesting. SEL is the type of a "selector" which identifies the name of a method (not the implementation). So, for example, the methods -[Foo count] and -[Bar count] both share a selector, namely the selector "count". A SEL is a pointer to a struct objc_selector, but what the heck is an objc_selector? Well, it's defined differently depending on if you're using the GNU Objective-C runtime, or the NeXT Objective-C Runtime (like Mac OS X). Well, it ends up that Mac OS X maps SELs to simple C strings. For example, if we define a Foo class with a - (int)blah method, the code NSLog(@"SEL = %s", @selector(blah)); would output SEL = blah.


IMP

From the header above IMP is declared as id (*IMP)(id, SEL, ...), so it's a pointer to a function that takes an id (the "self" pointer), the SEL that was called, and some other variable arguments.


Method

The Method type is defined in objc/objc-class.h as:

typedef struct objc_method *Method;
struct objc_method {
SEL method_name;
char *method_types;
IMP method_imp;
};

So, this kind of ties together some of the other types that we talked about. So, a method is a type that relates selectors and implementations.


Class

From above, Class is defined to be a pointer to a struct objc_class, which is declared in objc/objc-class.h as:

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;
};

I'm not going to get into much detail here, other than to show the declaration. We'll talk more about this in a future post.





Well, that's about it for now. These are all important types and concepts in Objective-C and I thought they would be good to talk about. More later...

3 comments:

Naaman said...

If I were to declare an object as id foo = nil, I don't understand how the compiler will know any more information about the object foo... such as the methods and variables assocated with it. Since id is a pointer that can point to any subclass of NSObject.

Greg said...

Well, you're right, the compiler won't know any additional information about the object. Objects in Objective-C are dynamically typed which basically means their actual type is determined at runtime. This is made possible because each instance (refer back to the definition of "struct objc_object", i.e. "id") has an "isa" pointer that points to the "Class" for the object. So, the runtime system can determine the actual type of any object at runtime by following the object's isa pointer, and from the isa pointer the runtime can figure out what selectors the instance knows about.

Now, you can use also use static typing in Objective-C by declaring objects to be of a more specific type than "id" (e.g. Foo *foo = nil). When you do this the compiler (rather than the runtime) can do some preliminary checks to see what messages an object may respond to, and issue a warning if an object doesn't respond to a message sent. Static typing is a good thing, and should be used as often as possible.

So, back to your question... declaring an object as "id foo = nil" will provide no additional information to the compiler. Did that answer the question or did I miss the boat? Or were you asking about sending a message to a nil object?

Naaman said...

Yes, that answered the question on the dot. Thank you... :-)