6

I'm trying to get a Method at runtime and then use its data structure to call it's implementation. Just to clarify, this is for learning purposes, not for any practical reason. So here is my code.

#import <Foundation/Foundation.h>
#import <stdio.h>

#import <objc/runtime.h>

@interface Test : NSObject 
-(void)method;
@end

@implementation Test

-(void)method {
    puts("this is a method");
}

@end 

int main(int argc, char *argv[]) {

    struct objc_method *t = (struct objc_method*) class_getInstanceMethod([Test class], @selector(method));
    Test *ztest = [Test new];

    (t->method_imp)(ztest, t->method_name);

    [ztest release];
    return 0;
}

The definition of struct objc_method is as follows (defined in objc/runtime.h)

typedef struct objc_method *Method;

....

struct objc_method {
    SEL method_name                                          OBJC2_UNAVAILABLE;
    char *method_types                                       OBJC2_UNAVAILABLE;
    IMP method_imp                                           OBJC2_UNAVAILABLE;
}                                                            OBJC2_UNAVAILABLE;

however when I try to compile my code, I get this error.

error: dereferencing pointer to incomplete type

But when I add this to my code (to explicitly declare an objc_method), it works just as expected.

struct objc_method {
    SEL method_name;
    char *method_types;
    IMP method_imp;
};

typedef struct objc_method* Method;

Could someone explain to me why my code works when I explicitly declare this structure, and not when I import it from objc/runtime.h? Does it have anything to do with OBJC2_UNAVAILABLE? I can't find a definition for that, but it is defined in my environment.

EDIT:

I ran gcc -E code.m -o out.m to see what OBJC2_UNAVAILABLE was getting replaced with, it turns out that OBJC2_UNAVAILABLE was defined as __attribute__((unavailable)) in my environment. Can someone explain what that means and why Method still works if this structure is "unavailable"?

John Corbett
  • 1,595
  • 10
  • 19

2 Answers2

6

I just took a good look at the objc runtime header and found what the problem is, and how to fix it :)

So if you look at the area of the file that contains the transparent definition of the structure, you'll see that it's in the body of:

#if !__OBJC2__

...

#endif

Since this IS defined, it means that the structure that we refer to is in fact forward-declared and opaque (and thus incomplete).

What we must do instead is use the functions provided for accessing these members:

IMP method_getImplementation(Method method);
SEL method_getName(Method method);
void method_getReturnType(Method method, char *dst, size_t dst_len);

Etc.

You can see the full list of accessor methods and plenty of other goodies in the Runtime Reference Guide.

Hope it helps!

mamackenzie
  • 1,156
  • 7
  • 13
5

Its fields were previously defined, but it's an opaque type in ObjC-2. Use the runtime instead, and do not define the fields yourself:

int main(int argc, char *argv[]) {

    Method t = class_getInstanceMethod([Test class], @selector(method));
    Test * ztest = [Test new];

    IMP imp = method_getImplementation(t);

    typedef void (*fn)(id,SEL);
    fn f = (fn)imp;
    f(ztest,@selector(method));

    [ztest release];
    return 0;
}
justin
  • 104,054
  • 14
  • 179
  • 226
  • just to clarify, opaque means that the structure is defined in the implementation file and not the header file, correct? – John Corbett Aug 02 '12 at 05:53
  • 1
    in the case of a c struct, opaque means that the name exists for you to use a handle/pointer to an instance of, but its contents/fields are hidden from you (the client). this is just like a `CFStringRef`, which you cannot access the fields of, but you may use it with CFString APIs. the memory behind that handle may be a formal structure, or it may be a C++ type or really an arbitrary memory blob which is defined by the implementation (and could very well vary across releases). you cannot "see into" an opaque object/type/instance. – justin Aug 02 '12 at 06:03
  • @JohnCorbett thanks - and i totally agree that michael's (+1) is the proper answer. take mine as a supplemental example. i omitted details because they existed in his answer when i posted the example. – justin Aug 02 '12 at 06:15