8

所以我来自 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);

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

4

2 回答 2

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方法,以便编译器知道发生了什么(请参阅此链接)。下面是一个采用一个参数并返回 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-请参阅此处了解更多信息

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

#import <objc/message.h>

或者

@import ObjectiveC.message;
于 2013-07-07T07:16:45.533 回答
4

通常,您应该尽可能将块视为更好的选择(它们成功了NSInvocation)。

至于返回值,你可以使用这个:

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

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

于 2013-07-07T07:06:14.363 回答