Search code examples
iosclassmethodstweak

iOS invoke class method with IMP


Say I am developing a tweak app, I want to create an instance of one Class whose header can't be imported, but I do know class name, class methods and instance methods How can I create it in class method with parameters? Say this Class called MMClass with class method

  +(instancetype)do:(NSString*)string for:(NSString *)antherString; 

What I am doing is as below:

Class class = objc_getClass("MMClass");
Method initMethod = class_getClassMethod(class,
                                             @selector(do:for:));
IMP imp = method_getImplementation(initMethod);
id instance = imp(class,@selector(do:for:),@"do",@"ye");

Is this right?


Solution

  • First, I'm not sure if I'm stating the obvious, but why not just create your own header with the declarations you want to use and import that (or just inline the declaration at the top of your file if you are only going to use it in one file)? and call the methods normally? instead of going through all this mess? All the compiler cares about is that it sees some declaration for the methods you want to call.

    When you call the actual method implementation using a function pointer, you need to cast it to the right type of function pointer corresponding to the signature of the method:

    Class class = objc_getClass("MMClass");
    Method initMethod = class_getClassMethod(class, @selector(do:for:));
    IMP imp = method_getImplementation(initMethod);
    id (*foo)(Class, SEL, NSString *, NSString *) =
        (id (*)(Class, SEL, NSString *, NSString *))imp;
    id instance = foo(class, @selector(do:for:), @"do", @"ye");
    

    But it's silly to get an IMP that you are only going to use once. Rather, you should just cast objc_msgSend to the desired function pointer type, and call that directly instead:

    Class class = objc_getClass("MMClass");
    id (*foo)(Class, SEL, NSString *, NSString *) =
        (id (*)(Class, SEL, NSString *, NSString *))objc_msgSend;
    id instance = foo(class, @selector(do:for:), @"do", @"ye");