【问题标题】:Why does NSInvocation getReturnValue: lose object?为什么 NSInvocation getReturnValue: 丢失对象?
【发布时间】:2011-08-16 12:20:29
【问题描述】:

我需要你的帮助。我对 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个字节的数据。那么应该由 'buttonWithType:' 初始化的 UIButton 对象会发生什么?

得到一些答案后补充:

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

会因为这个而发生吗?

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

没错吧?

【问题讨论】:

    标签: objective-c pointers nsinvocation


    【解决方案1】:

    如果您使用的是 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 转移所有权。

    【讨论】:

    • 这对我有用,但我必须在调用 CFRetain 之前检查结果是否为零。
    • 该死,这就是我讨厌ARC的那种东西:
    • 我采用的一种做法是避免使用 NSInvocation 而是使用块。代码更简洁,让 ARC 满意的麻烦更少,而且更灵活。
    • @spstanley,谢谢!! - 花了最后两个小时试图追查这个!
    • @spstanley,你是我的英雄!
    【解决方案2】:

    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");
    

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

    【讨论】:

    • 谢谢。是的,我想知道结果类型是否为 UIButton,但是当我使用 resObj 或在我的情况下为 pushButton5 时,它会导致 EXC_BAD_ACCESS。
    • 可能该方法没有返回 Obj-C 对象。尝试检查methodReturnType
    【解决方案3】:

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

    【讨论】:

    • 我没有告诉 buttonWithType: 返回对象。我的意思是当我试图以这种方式获取对象时:(*result),大小不等于UIButton 的大小。但正如 Yuji 伤心“...sizeof() 不知道 result 在运行时指向什么”。
    【解决方案4】:

    这不是问题。

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

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

    应该是

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

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

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

    【讨论】:

    • 谢谢我修好了。关键是在这段代码id pushButton5 = (id) result;之后,当我尝试使用pushButton5时,它会导致EXC_BAD_ACCESS。这就是为什么我需要检查尺寸。
    【解决方案5】:

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

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

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-07-23
      • 1970-01-01
      • 2021-01-02
      • 1970-01-01
      • 1970-01-01
      • 2011-03-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多