为什么NSInvocation getReturnValue:丢失对象?

时间:2011-08-16 12:20:30

标签: objective-c pointers nsinvocation

我需要你的帮助。我在NSInvocation'getReturnValue:'方法中遇到了一些问题。我想以编程方式创建UIButton,甚至更多,我想使用NSInvocation动态创建它并通过NSArray传递值(这就是我包装UIButtonTypeRoundedRect的原因)。

清单。

NSLog(@"Button 4 pushed\n");//this code executed when button pushed
Class cls = NSClassFromString(@"UIButton");//if exists {define class},else cls=nil
SEL msel = @selector(buttonWithType:);
//id pushButton5 = [cls performSelector:msel withObject:UIButtonTypeRoundedRect];//this code works correctly,but I want to do this by NSInvocation
//---------------------------
NSMethodSignature *msignatureTMP;
NSInvocation *anInvocationTMP;

msignatureTMP = [cls methodSignatureForSelector:msel];
anInvocationTMP = [NSInvocation invocationWithMethodSignature:msignatureTMP];
[anInvocationTMP setTarget:cls];
[anInvocationTMP setSelector:msel];

UIButtonType uibt_ = UIButtonTypeRoundedRect;
NSNumber *uibt = [NSNumber numberWithUnsignedInt:uibt_];
NSArray *paramsTMP;
paramsTMP= [NSArray arrayWithObjects:uibt,nil];

id currentValTMP = [paramsTMP objectAtIndex:0];//getParam from NSArray
NSInteger i=2;
void* bufferTMP;

//if kind of NSValue unwrapp it.
if ([currentValTMP isKindOfClass:[NSValue class]]) {
    NSUInteger bufferSize = 0;
    NSGetSizeAndAlignment([currentValTMP objCType], &bufferSize, NULL);
    bufferTMP = malloc(bufferSize);
    [currentValTMP getValue:bufferTMP];//copy currentVal to bufer
    [anInvocationTMP setArgument:bufferTMP atIndex:i];// The +2 represents the (self) and (cmd) offsets
}else {
    [anInvocationTMP setArgument:&currentValTMP atIndex:i];//Again,+2 represents (self) and (cmd) offsets
}
void* result = malloc([[cls methodSignatureForSelector:msel] methodReturnLength]);
[anInvocationTMP invoke];
[anInvocationTMP getReturnValue:result];

NSLog(@"sizeof(UIButton)=%i,sizeof(result)=%i,methodreturnlength = %i,sizeof(*result)=%i",class_getInstanceSize(NSClassFromString(@"UIButton")),sizeof(result),[[cls methodSignatureForSelector:msel] methodReturnLength],sizeof(*result));

id pushButton5;
pushButton5=result;
//---------------------------

NSLog输出: sizeof(UIButton)= 140,sizeof(result)= 4,methodreturnlength = 4,sizeof(* result)= 1

问题是来自NSInvocation的值是大小为4个字节的指针。它应该指向UIButton对象,大小为140字节。但实际上是指1字节数据。那么UIButton对象会发生什么呢?应该用'buttonWithType:'?

来初始化

获得一些答案后添加:

澄清一下:我想获得UIButton个对象,但在此代码id pushButton5 = (id) result;之后,当我尝试使用pushButton5时,会导致EXC_BAD_ACCESS。有人能帮助我吗?

这可能因为这个而发生吗?

Class cls = NSClassFromString(@"UIButton");
...
[anInvocationTMP setTarget:cls];

这是对的,不是吗?

5 个答案:

答案 0 :(得分:18)

如果你正在使用ARC,我会替换它:

void* result = malloc([[cls methodSignatureForSelector:msel] methodReturnLength]);
[anInvocationTMP invoke];
[anInvocationTMP getReturnValue:result];

有了这个:

CFTypeRef result;
[anInvocationTMP invoke];
[anInvocationTMP getReturnValue:&result];
if (result)
    CFRetain(result);
UIButton *pushButton5 = (__bridge_transfer UIButton *)result;

原因是invokation的返回对象没有被保留,所以它会消失,即使你立即将它分配给一个对象引用,除非你先保留它然后告诉ARC转移所有权。

答案 1 :(得分:1)

result的类型为void*,您的sizeof(*result)表达式正在测量sizeof(void),这显然会在您的编译器中产生1。

要检查Objective-C对象的类型,请使用isKindOfClass:方法:

id resObj = (id)result;
if ([resObj isKindOfClass:[UIButton class]])
    NSLog(@"mazel tov, it's a button");

请确保它首先是一个客观的c对象。

答案 2 :(得分:0)

返回值是UIButton *而不是UIButton。因此,代码中的所有内容都很好。

答案 3 :(得分:0)

这不是问题。

首先,getReturnValue:应该在调用之后。所以,

[anInvocationTMP getReturnValue:result];
[anInvocationTMP invoke];

应该是

[anInvocationTMP invoke];
[anInvocationTMP getReturnValue:result];

接下来,您应该忘记UIButton本身的大小。 buttonWithType返回的内容为UIButton*,而不是UIButton。在Objective-C中,您永远不应该直接处理对象本身。您应该始终使用指向该对象的指针。

最后,(Objective-)C的sizeof运算符纯粹是编译时操作。因此,sizeof(*result)根本不知道result在运行时指向的内容。但这没关系......你不应该关心UIButton的大小,正如我已经告诉你的那样。

答案 4 :(得分:0)

真的回答我需要的是......你觉得在哪里?是的,在文档中。

  • 使用NSMethodSignature方法methodReturnLength确定缓冲区所需的大小:

    NSUInteger length = [[myInvocation methodSignature] methodReturnLength];
    buffer = (void *)malloc(length);
    [invocation getReturnValue:buffer];
    
  • 当返回值是对象时,将指针传递给应放置对象的变量(或内存):

    id anObject;
    NSArray *anArray;
    [invocation1 getReturnValue:&anObject];
    [invocation2 getReturnValue:&anArray];
    

所以问题解决了。谢谢你的回复。