I guess one way of describing protocol methods is as pure virtual functions in an abstract class that does not exist. This is important because Objective-C does not have multiple inheritance, but it does let you attach multiple protocols to a class, thus effectively allowing your class to "inherit" multiple abstract classes. For real inheritance, any the derived class automatically conforms to any protocols implemented in the base class.
Categories are really funky: they allow you to extend a class without creating a new class: all the methods and protocols declared in the category become available to the rest of the system via the original class, even without access to that class's source code. One of the examples I've seen is to add the ability for a class to print itself. The original class has no concept of printing and if you pass it to the printing system, you get either a no-op or an error message. Using a category to add printing, you can then pass the exact same class to the printing system and the class will be printed.
One of the reasons for @ is to keep the name-space clean. ie, implementation, end, class etc remain available for use as identifiers. @"somestring" is short-hand for creating a string object from a string constant. ie @"string" is the same as [NSString fromString:"string"] (or something like that, I don't know the details off-hand). Objective-C is funny in one way: any word is usable in selector names (keywords, typedefs, other class names...), eg -acceptMessage:(SEL) msg for:(id) obj; (though forObject would be more informative, of course).
The reason I added Objective-C to qfcc was rhamph was complaining about the utter mess of overloaded entity fields in CustomTF (prozac flavor, iirc), and I first added structs and unions. After a lot of discussion in #quakeforge, it was decided that objects would be nice, but I dreaded the idea of trying to add C++ extensions to QC. Deek suggested Objective-C, I got hold of a good pdf specification of the language (possibly through Deek), and decided it looked easy enough. I committed the first stab at the parser mods 7th of May, 2002, and and basic test code running 31st of the same month. Parser, code generation, structure writing for the runtime information, the runtime system itself, and a zillion little changes in the engine and compiler to support it all. However, I'm still fixing bugs in it (oversights, not yet implemented features, mis-interpretations, etc), but overall, it's been more than good enough.
One cool feature of of QF's runtime is that the server checks for a field named ".this" (@this in qc source: just use it, auto-declaration equivalent to ".id .this" ) and if present, passes the value stored in the entity's .this field as the first parameter, nil as the second and other (if relevant) as the third to the appropriate think/touch/blocked function. One would then assign the object's pointer to self.@this (or better, @self.@this (so Objective-C's self doesn't clash with QC's self)) at spawn time, and the set @self.think to the IMP of the appropriate messge.
server code:
Code: Select all
static inline void
sv_pr_think (edict_t *self)
{
pr_int_t this;
*sv_globals.self = EDICT_TO_PROG (&sv_pr_state, self);
*sv_globals.other = 0;
if ((this = sv_pr_state.fields.this) != -1) {
PR_RESET_PARAMS (&sv_pr_state);
P_INT (&sv_pr_state, 0) = E_POINTER (self, this);
P_INT (&sv_pr_state, 1) = 0;
P_INT (&sv_pr_state, 2) = 0;
}
PR_ExecuteProgram (&sv_pr_state, SVfunc (self, think));
}
Code: Select all
local IMP imp = [self methodForSelector: @selector (waypointThink)];
waypoint_thinker.think = (void ()()) imp; //if this was C, that would be void (*)() yay for qc function variables
waypoint_thinker.nextthink = time;
waypoint_thinker.@this = self; //NOTE this is the method's self, not "entity self" which would be "@self"
Code: Select all
@self = spawn ();
@self.origin = origin;
Heh, this post really should be in another thread: we're way beyond "mundane C tricks" and into "esoteric QC and engine hacks"