Re: Confusion
Re: Confusion
- Subject: Re: Confusion
- From: Bill Bumgarner <email@hidden>
- Date: Sun, 24 Jun 2001 23:20:12 -0400
A simple clarification (I hope). This is the ANSI C type definition
for the data type of id.
typedef struct objc_object {
Class isa;
} *id;
What this indicates is that the type "id" is a POINTER (hence the "*")
to a structure that contains a single member; the isa. The isa is of
type Class. Class is defined as:
typedef struct objc_class *Class;
That is, a Class is a pointer to a struct of type objc_class.
objc_class is a struct that defines an objective c class.
At this point, we are deep in the bowels of the ObjC runtime-- someplace
that most ObjC/Cocoa developers really never need to visit, but it is
both very interesting and necessary to understand if one wants to have a
Complete Picture of What is Really Going On.
---
The key to all of this is the isa. The isa is something that is common
to everything ObjC. In particular, if you define:
id fooThing;
NSString *barThing;
This could be rewritten as:
struct objc_object *fooThing;
NSString *barThing;
And it would mean exactly the same thing. Now, look at the declaration
for NSObject:
@interface NSObject <NSObject> {
Class isa;
}
.... methods here ...
@end
Ignore the <NSObject> for the moment-- it has to do with the methods
implemented by NSObject and isn't important for this discussion.
However, notice two things:
- NSObject does *not* declare a superclass. It is a root class.
- The first and only instance variable is exactly the same as the first
and only instance variable in the objc_object structure. As far as the
compiler is concerned, an (objc_object *) is pretty much identical to an
(NSObject *).
--
Now, have a look at the declaration for objc_msgSend():
id objc_msgSend(id self, SEL op, ...);
This could be rewritten as:
struct objc_object *objc_msgSend(struct objc_object *self, SEL
op, ...);
That is, objc_msgSend() is a function that takes at least two
arguments-- the target of the method call and the name of the method to
be invoked-- and generally returns a pointer (a reference) to an
object. Generally indicates that sometimes a method returns an
undefined value as is the case with a method that has a return type of
(void). Actually, it is more confusing in that objc_msgSend() will
actually return lots of different data types-- (char), (int), (float),
etc...-- and the compiler takes care of doing the right thing. Great
for compilation and writing code, sucks for dynamic diddling of the
runtime.... but that is really neither here nor there in this context.
So, if you type:
x = [fooThing objectAtIndex: 12];
The compiler literally translates that to:
x = objc_msgSend(fooThing, @selector(objectAtIndex:), 12);
As a matter of fact, early implementations of ObjC were nothing more
than an extension to the C precompiler. It literally would interpret
the [fooThing objectAtIndex: 12] and turn it into the above function
call.
The fact that fooThing is declared as a variable of type (id), declared
as type (struct objc_object *), or declared as (NSArray *) makes no
actual difference to the resulting compiled binary or to program
execution. By specifying (NSArray *), it gives the compiler a hint as
to the scope of methods that might be valid in that context.
--
To bring this back around...
The isa pointer contained within every instance-- be it declared any of
the three ways demonstrated above-- contains all of the information used
by the runtime to figure out what method to invoke. A Class is defined
as:
typedef struct objc_class *Class;
And the objc_class struct 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;
#if defined(Release3CompatibilityBuild)
struct objc_method_list *methods;
#else
struct objc_method_list **methodLists;
#endif
struct objc_cache *cache;
struct objc_protocol_list *protocols;
};
Relevant to this conversation, the isa pointer contains all of the
information that describes the instance of whatever the (id) or
(NSString *) or (struct objc_object *) points to! It contains
everything necessary for the objc_msgSend() function to dispatch a
message to the appropriate method implementation.
---
The key is to separate the concepts of "at compile time" and "at
runtime". A lot of the above is "at runtime": that is, an object
reference or pointer is passed off to objc_msgSend() along with the name
of the method to be invoked and the arguments to be sent to the
method. objc_msgSend() uses the isa information to find the
implementation and execute it. There is no difference between (id) and
(NSString*) at runtime.
At compile time, the difference is that the compiler uses the
differences between (id) and (NSString *) to provide you-- the
developer-- with as much information as is possible regarding what can
and cannot be done to a particular object reference.
b.bum