3

我需要你的帮助。我对 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];

这是正确的,不是吗?

4

5 回答 5

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;

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

于 2012-07-19T21:02:05.357 回答
1

result有 typevoid*并且你的sizeof(*result)表达式是 measuing sizeof(void),这显然会在你的编译器中产生 1 。

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

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

首先确保它确实是一个objective-c对象。

于 2011-08-16T12:49:45.193 回答
0

返回值是 UIButton* 而不是 UIButton。因此,您的代码中的一切看起来都很好。

于 2011-08-16T12:25:44.493 回答
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正如我已经告诉过你的那样,你不应该关心 的大小。

于 2011-08-16T12:31:18.653 回答
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];
    

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

于 2011-08-17T07:58:48.540 回答