Skip to content

Calling a method, that's been overridden by a category

Nat! edited this page Mar 29, 2017 · 5 revisions

This is a new feature in mulle-objc 0.4.x

This is an example class:

Foo.h:

@interface Foo
+ (void) hello;
@end

Foo.m:

#include <mulle_objc/mulle_objc.h>
#include <stdio.h>

@implementation Foo
+ (void) hello
{
   printf( "Helo");
}
@end

And a category on Foo fixes the misspelling:

Foo+TypoFix.m:

#include <mulle_objc/mulle_objc.h>
#include <stdio.h>

@implementation Foo( TypoFix)
+ (void) hello
{
   printf( "Hello");
}
@end

But then we need another category to add the missing linefeed. Let's do this by calling +[Foo(TypoFix) hello] first and then just printing the linefeed.

Foo+LFFix.m:

#include <mulle_objc/mulle_objc.h>
#include <stdio.h>

@implementation Foo( LFFix)

+ (SEL *) categoryDependencies
{
    static SEL   dependencies[] = {  @selector( TypoFix), 0 };
    return( dependencies);
}

+ (void) hello
{
   struct _mulle_objc_class    *cls;
   struct _mulle_objc_class    *category_cls;
   struct _mulle_objc_runtime  *runtime;
   struct _mulle_objc_method   *this_method;
   struct _mulle_objc_method   *previous_method;

   //
   // first we look for the method, that represents the currently executing method
   // ask the runtime to find @selector( hello) in the category TypoFix of class 
   // Foo. self could be a subclass of Foo, so we need to find the actual Foo 
   // class first (and it's metaclass for +methods)
   // 

   // Do what +[Foo class] does.
   // Since the names of methods, categories, protocols and classes are
   // all hashed the same wa, we can use @selector to create a  
   // classid

   cls          = _mulle_objc_object_get_isa( self);    
   runtime      = _mulle_objc_class_get_runtime( cls);

   category_cls = mulle_objc_runtime_unfailing_lookup_class( runtime,
                                                             @selector( Foo));

   // get the meta class though
   category_cls = _mulle_objc_class_get_metaclass( category_cls);

   //
   // Now search through all the categories, of Foo, Since categories are unique
   // this guarantees that our category is found. Notice that we give the
   // inheritance specified by the "self" class as the original search would have. 
   //
   this_method = _mulle_objc_class_search_method( category_cls, 
                                                  _cmd, // this is @selector( hello)
                                                  NULL, // no previous method to skip
                                                  (void *) @selector( TypoFix), 
                                                  _mulle_objc_class_get_inheritance( cls));
   assert( this_method);  // how could it fail ? :)

   //
   // Now that we have this method `+[Foo( LFFix) hello]` we can ask for the 
   // overriden method. Since we are base class to the caller (and due to the 
   // way the search goes down from subclass to superclass) we can start at 
   // category_cls again.
   // This does the whole search again, but will skip over our method.
   //
   previous_method = _mulle_objc_class_search_method( category_cls, 
                                                      _cmd, 
                                                      this_method, 
                                                      MULLE_OBJC_ANY_OWNER,
                                                     _mulle_objc_class_get_inheritance( cls));
   // if found, call it
   if( previous_method)
      (*_mulle_objc_method_get_implementation( previous_method))( cls, _cmd, NULL);
   printf( "\n");
}
@end

Read Understanding load order of classes and categories to learn about +categoryDependencies.

Call Chaining

If you want to chain all previous methods together you could execute:

while( previous_method = _mulle_objc_class_search_method( cls, 
                                                          _cmd, 
                                                          this_method), 
                                                          MULLE_OBJC_ANY_OWNER,
                                                          _mulle_objc_class_get_inheritance( cls))
{
   (*_mulle_objc_method_get_implementation( previous_method))( cls, _cmd, NULL);
   this_method = previous_method;
}

This could be made faster. But it's probably never gonna be really fast or competative with super calls. How could a language construct look like ? A new keyword like super ?