-
Notifications
You must be signed in to change notification settings - Fork 8
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
.
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 ?