MulleEOF - Augmentation Style Programming IV
The conclusion to the issue I verbosely circled around in the last three entries of this weblog.
In the first three parts, I wanted to show how it would be nice for a library writer to have the possibility to extend a class with instance variables. The language Objective-C itself offers no support for that.
A solution on the language level isn't easy, and the problem is somewhat similiar to that posed by multiple inheritance. How to order and access the instance variables ? As the category load order is determined at runtime, this can't be solved at compile time or link time at all. The compiler can only assume, that this category is the only category adding instance variables and compile for those instance variable offsets. Everything else has to be done and fixed at runtime. So the runtime linker would have to patch the different offsets into the compiled instructions. If the offset of an augmented instance variable 'x_' was known to be 32 at compile time and an intermediate category introduced two 32 bit instance variable augmentations, every load and store instruction accessing 'x_' would need to have their offsets patched by adding 8.
That suffices only if the addressing is simple. Since you can do some fairly elaborate arithmetic with offset_of and the like, one would probably need to have an expression evaluator in the runtime linking stage (which brings back fond memories of my first assembler, linker which had just such a feature) ... a major operation, that is unlikely to happen.
Then there is the problem of different sized instances. If you allocate an instance before an augmenting category is loaded in, its size will be less than of those instances allocated after the load. Access of such a smaller size instance by augmented category methods expecting their added instance variables is of course catastrophic. One could "finalize" a class after it's first alloc, but that restriction takes away from the attractiveness of augmentation. Thinking up some dynamic subclassing scheme is probably fruitless and defeats the idea of categories.
A cheap alternative
A cheap alternative to all that complicated runtime trickery is using a NSMutableDictionary either as a container to hold all the instance variables or as a designed-in possiblity to hold the augmented instance variables. Obviously this is a feature that must be thought of ahead of time for classes, that are to be shared - as library classes are wont to be. And predicting the needs of other people is impossible, so you would be tempted to put it in all classes...
If you hold all instance variables in an
NSMutableDictionary style storage scheme, you might enjoy
the theoretical advantage of less storage needs, if the
instance variables values are mostly nil. Then you would not need
to store them in the dictionary at all. It could be tempting to
subclass such classes from NSMutableDictionary directly,
and why not.
With the use of NSMutableDictionary I say theoretical, because the overhead of the key/value storage will negate that effect in all but the most unlikely circumstances. One can think of more complicated, yet efficient variations of the NSMutableDictionary method for holding the data (key storage "in" the class, storage in a malloced C-Array) that could actually make good of the promise of reduced storage requirements, but likely wont also.
Or there could be a designated instance variable of type NSMutableDictionary that is used for the augmentation dictionary. A side effect of this is, that it divides the instance variables into first class and second class citizens. The augmented variables incur a speed penalty relative to the classed instance variables and that may or may not be acceptable.
Why this doesn't work well
The main stumbling block is now, that the instance variables can not be initialized during init (and released during dealloc, though when the dictionary goes down, the instance variables will also get their release, but no custom code will run). The problem of patching into init is equally difficult to the problem of adding an instance variable to the class. Overriding init in the category is so obviously a bad idea that I spare myself the pain of writing down the bad effects that would have. Calling an initXXX method from init would defeat the purpose of the augmenting category, as knowledge about that category should not exist in the class.
So the only solution appears to me, that one would need some sort of NSMutableDictionary style infrastructure to register custom init methods (possibly during +load) of the categories somewhere in a class variable - and since those do not exist in static storage, maintained by some class methods -. Custom code in the init method would then have to call the various init functions. This is quite an elaborate setup. If I weren't in Marocco with no Mac in sight, I might code up an example but I leave that as an exercise to the reader :P
I am currently as of today not doing any of this, because it feels like too much effort for one or two added instance variables in the whole MulleEOF project.