NSInvocation和内存问题

时间:2013-07-07 06:01:27

标签: ios objective-c cocoa-touch memory-management nsinvocation

所以我来自Java世界,我们对内存管理问题一无所知。在大多数情况下,ARC已经救了我的屁股,但这里有些让我难过的东西。基本上我使用NSInvocations来做一些事情,在进行下面的代码修改之前,我遇到了一些讨厌的内存问题。由于我做了这些修改,内存崩溃已经消失,但我通常非常害怕我不理解的代码。我这样做了吗?

之前:各种各样的记忆问题:

NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[[target class] instanceMethodSignatureForSelector:selector]];
[invocation setSelector:selector];
[invocation setTarget:target];
[invocation setArgument:&data atIndex:2];
[invocation setArgument:&arg atIndex:3];
[invocation invoke];

NSString *returnValue;
[invocation getReturnValue:&returnValue];

之后:没有记忆问题,但我不确定我是否做对了:

NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[[target class] instanceMethodSignatureForSelector:selector]];
[invocation setSelector:selector];
[invocation setTarget:target];
[invocation setArgument:&data atIndex:2];
[invocation setArgument:&arg atIndex:3];
[invocation invoke];

CFTypeRef result;
[invocation getReturnValue:&result];

if (result)
    CFRetain(result);

NSString *returnValue = (__bridge_transfer NSString *)result;

编辑:

我只是想根据下面的答案添加,我使用了objc_msgSend,因此。:

NSString * returnValue = objc_msgSend(target, selector, data, arg);

它解决了所有内存问题,而且看起来更简单。如果您发现此问题,请发表评论。

2 个答案:

答案 0 :(得分:5)

我会回答你这样的问题:不要使用NSInvocation这是一个友好的建议,以避免,如果可能的话

在Objective-C中有很多很好的方法可以做回调,这里有两个对你有用的方法:

  • :在上下文中定义,选择任何参数计数和类型,也可能存在内存问题。关于如何使用它们有很多资源。
  • performSelector :最多2个对象参数,使用以下方法调用:

    [target performSelector:selector withObject:data withObject:args];
    

此外,当我需要调用带有4个参数的选择器时,我仍然不使用NSIvocation,而是直接调用objc_msgSend

id returnValue = objc_msgSend(target, selector, data, /* argument1, argument2, ... */);

简单。

编辑: 使用objc_msgSend时,您需要注意返回值。如果您的方法返回一个对象,请使用上面的方法。如果它返回一个基本类型,则需要转换objc_msgSend方法,以便编译器知道发生了什么(see this link)。这是一个带有一个参数并返回BOOL的方法的示例:

// Cast the objc_msgSend function to a function named BOOLMsgSend which takes one argument and has a return type of BOOL.
BOOL (*BOOLMsgSend)(id, SEL, id) = (typeof(BOOLMsgSend)) objc_msgSend;
BOOL ret = BOOLMsgSend(target, selector, arg1);

如果你的方法返回一个结构,事情会有点复杂。您可能(但并非总是如此)需要使用objc_msgSend_stret - see here for more info

编辑: - 此行必须添加到代码中,否则Xcode会抱怨:

#import <objc/message.h>

@import ObjectiveC.message;

答案 1 :(得分:4)

您应该尽可能地将块视为优越的替代方案(它们成功NSInvocation)。

就返回值而言,您可以使用:

CFTypeRef result = NULL;
[invocation getReturnValue:&result];    
NSString *returnValue = (__bridge NSString *)result;

这里的根本问题是,就ARC而言,-getReturnValue:不会返回out 对象。因此,可能导致引用计数操作错误(编译器在ARC中为您添加这些操作),因为-getReturnValue:的参数是void*,而不是out对象(例如NSObject**