我有一个返回CGSize
的类方法,我想通过Objective-C运行时函数调用它,因为我将类和方法名称作为字符串值。
我在XCode 4.2中使用ARC标志进行编译。
方法签名:
+(CGSize)contentSize:(NSString *)text;
我尝试的第一件事是用这样的objc_msgSend
调用它:
Class clazz = NSClassFromString(@"someClassName);
SEL method = NSSelectorFromString(@"contentSize:");
id result = objc_msgSend(clazz, method, text);
这与" EXC_BAD_ACCESS"没有堆栈跟踪。我首先使用了这个,因为objc_msgSend
的文档说明了
当遇到方法调用时,编译器会生成对其中一个函数
objc_msgSend
,objc_msgSend_stret
,objc_msgSendSuper
或objc_msgSendSuper_stret
的调用。 [...]使用objc_msgSendSuper_stret
和objc_msgSend_stret
发送将数据结构作为返回值的方法。
接下来,我使用objc_msgSend_stret
这样:
Class clazz = NSClassFromString(@"someClassName);
SEL method = NSSelectorFromString(@"contentSize:");
CGSize size = CGSizeMake(0, 0);
objc_msgSend_stret(&size, clazz, method, text);
使用上面的签名给出了以下两个编译器错误和两个警告:
错误:自动引用计数问题:非Objective-C指针类型的隐式转换' CGSize *' (又名' struct CGSize *')to' id'不允许使用ARC
警告:语义问题:不兼容的指针类型传递' CGSize *' (又名' struct CGSize *')参数类型' id'
错误:自动引用计数问题:将Objective-C指针隐式转换为' SEL'不允许使用ARC
警告:语义问题:不兼容的指针类型传递 ' __ unsafe_unretained Class'到' SEL'
类型的参数
如果我看一下方法的声明,那就是:
OBJC_EXPORT void objc_msgSend_stret(id self, SEL op, ...)
__OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
与objc_msgSend
相同:
OBJC_EXPORT id objc_msgSend(id self, SEL op, ...)
__OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
这解释了编译器错误,但是我在运行时使用什么来在运行时调用类及其静态方法并返回结构值?
答案 0 :(得分:16)
您需要将objc_msgSend_stret
强制转换为正确的函数指针类型。它被定义为void objc_msgSend_stret(id, SEL, ...)
,这是一种不适合实际调用的类型。你会想要使用像
CGSize size = ((CGSize(*)(id, SEL, NSString*))objc_msgSend_stret)(clazz, @selector(contentSize:), text);
这里我们只是将objc_msgSend_stret转换为(CGSize (*)(id, SEL, NSString*))
类型的函数,这是实现IMP
的{{1}}的实际类型。
注意,我们也使用+contentSize:
,因为没有理由将@selector(contentSize:)
用于编译时已知的选择器。
另请注意,即使是NSSelectorFromString()
的常规调用,也需要转换函数指针。即使调用objc_msgSend()
直接在您的特定情况下工作,它依赖于varargs方法调用ABI与调用非varargs方法的ABI相同的假设,这在所有平台上都可能不正确。 / p>
答案 1 :(得分:7)
如果您希望从类方法中检索结构,可以按如下方式使用NSInvocation:
Class clazz = NSClassFromString(@"MyClass");
SEL aSelector = NSSelectorFromString(@"testMethod");
CGSize returnStruct; // Or whatever type you're retrieving
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[clazz methodSignatureForSelector:aSelector]];
[invocation setTarget:clazz];
[invocation setSelector:aSelector];
[invocation invoke];
[invocation getReturnValue:&returnStruct];
最后,returnStruct
应包含您的结构值。我刚刚在支持ARC的应用程序中对此进行了测试,这种方法很好。