如何从ARC下的运行时定义的类方法返回结构值?

Jon*_*ner 8 struct objective-c objective-c-runtime class-method automatic-ref-counting

我有一个返回a的类方法CGSize,我想通过Objective-C运行时函数调用它,因为我将类和方法名称作为字符串值.

我正在使用XCode 4.2中的ARC标志进行编译.

方法签名:

+(CGSize)contentSize:(NSString *)text;
Run Code Online (Sandbox Code Playgroud)

我尝试的第一件事是用objc_msgSend这样调用它:

Class clazz = NSClassFromString(@"someClassName);
SEL method = NSSelectorFromString(@"contentSize:");

id result = objc_msgSend(clazz, method, text);
Run Code Online (Sandbox Code Playgroud)

这与"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);
Run Code Online (Sandbox Code Playgroud)

使用上面的签名给出了以下两个编译器错误和两个警告:

错误:自动引用计数问题:ARC不允许将非Objective-C指针类型'CGSize*'(又名'struct CGSize*')隐式转换为'id'

警告:语义问题:不兼容的指针类型将'CGSize*'(又名'struct CGSize*')传递给'id'类型的参数

错误:自动引用计数问题:ARC不允许将Objective-C指针隐式转换为"SEL"

警告:语义问题:不兼容的指针类型将'__unsafe_unretained Class'传递给'SEL'类型的参数

如果我查看方法的声明,它是:

OBJC_EXPORT void objc_msgSend_stret(id self, SEL op, ...)
    __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
Run Code Online (Sandbox Code Playgroud)

这与objc_msgSend:

OBJC_EXPORT id objc_msgSend(id self, SEL op, ...)
    __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
Run Code Online (Sandbox Code Playgroud)

这解释了编译器错误,但是我在运行时使用什么来在运行时调用类及其静态方法并返回结构值?

Lil*_*ard 16

您需要转换objc_msgSend_stret为正确的函数指针类型.它定义为void objc_msgSend_stret(id, SEL, ...),实际调用的类型不合适.你会想要使用类似的东西

CGSize size = ((CGSize(*)(id, SEL, NSString*))objc_msgSend_stret)(clazz, @selector(contentSize:), text);
Run Code Online (Sandbox Code Playgroud)

这里我们只是投放objc_msgSend_stret到类型的函数(CGSize (*)(id, SEL, NSString*)),这是实际类型的IMP实现+contentSize:.

注意,我们也在使用,@selector(contentSize:)因为没有理由NSSelectorFromString()在编译时使用已知的选择器.

另请注意,即使是常规调用,也需要转换函数指针objc_msgSend().即使objc_msgSend()直接调用在您的特定情况下工作,它仍然依赖于varargs方法调用ABI与调用非varargs方法的ABI相同的假设,这在所有平台上可能都不正确.

  • @JonasGardner:我认为这实际上取决于结构的大小和相关的架构.您关心的架构上的`CGSize`很可能是一个简单的`objc_msgSend()`.您需要查阅体系结构ABI文档,以了解哪些结构大小/类型将在寄存器中返回而不是在堆栈上返回. (4认同)

Bra*_*son 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];
Run Code Online (Sandbox Code Playgroud)

在这结束时,returnStruct应该包含您的struct值.我刚刚在支持ARC的应用程序中对此进行了测试,这很好用.