6

test.m这是我用来测试行为的独立文件。

编译:clang test.m -o test.app -fobjc-arc -ObjC -framework Foundation. 确保安装了 Xcode 命令行工具。

#import <Foundation/Foundation.h>

@protocol Protocol

@optional
- (id)objProxyMethod;

@end

@interface ReturnObject: NSObject

@end

@interface Test : NSObject <Protocol>

@end

@interface Proxy : NSObject <Protocol>

- (id)objProxyMethod;

@end

@implementation ReturnObject

- (void)dealloc {
    NSLog(@"ERROR:");
    NSLog(@"I'm getting deallocated!");
    NSLog(@"This shouldn't happen!");
}

- (NSString *)description {
    return @"Blank object!";
}

@end

@implementation Proxy

- (id)objProxyMethod {
    NSLog(@"in [Proxy objProxyMethod]!");
    return [[ReturnObject alloc] init];
}

@end

@implementation Test

- (void)forwardInvocation:(NSInvocation *)invocation {
    NSLog(@"Forwarded invocation!");
    Proxy *proxy = [[Proxy alloc] init];
    [invocation invokeWithTarget: proxy];
    NSUInteger length = [[invocation methodSignature] methodReturnLength];
    if (length == 8) {
        id result;
        [invocation getReturnValue:&result];
    }
}

@end

int main () {
    Test *test = [[Test alloc] init];
    id objResult = [test objProxyMethod];
    NSLog(@"objResult = \"%@\"", objResult);

    return 0;
}

如果我注释掉[invocation getReturnValue:&result];,返回的对象不会被dealloc吃掉。我不知道这是否是一个错误,或者只是我误解了它的NSInvocation工作原理。

4

3 回答 3

24

问题是result默认__strong情况下,所以当它超出范围时,编译器会release为它生成一个。但是getReturnValue:没有给你返回对象的所有权,所以你的方法不应该释放它。

您可以通过更改以下声明来解决此问题result

__unsafe_unretained id result;

这可以防止编译器在超出范围时生成releasefor 。如果需要保留它,可以将其复制到另一个变量。resultresult__strong

您还可以添加一个类别来NSInvocation为您处理此问题:

@interface NSInvocation (ObjectReturnValue)

- (id)objectReturnValue;

@end

@implementation NSInvocation (ObjectReturnValue)

- (id)objectReturnValue {
    __unsafe_unretained id result;
    [self getReturnValue:&result];
    return result;
}

@end

...
    if (length == 8) {
        id result = [invocation objectReturnValue];
    }
...

您也可以将此报告为错误。我希望编译器,或者至少是静态分析器,会警告您正在将指向强指针的指针转换为id空指针。http://bugreport.apple.com

于 2012-08-08T22:40:57.607 回答
4

这是因为 ARC 无法管理写成指针的对象。只能直接赋值。

错误的:

id result;
[invocation getReturnValue:&result];

正确的:

void *pointer;
[invocation getReturnValue:&pointer];

id result = (__bridge id)pointer; //Correct, ARC will retain pointer after assignment
于 2014-01-29T08:55:42.017 回答
0
if (length == 8) {
    id result; //this is nil (its also a stack allocated pointer)
    [invocation getReturnValue:&result];  //sets the value to an object
}

...method ends object is deallocated

您必须将结果设置为未分配堆栈或不调用 getReturnValue 的指针。

API 可能会假设,因为您调用了 getReturnValue,您将保留(并可能使用返回值)。你没有。当您删除 getReturnValue 时,返回值是否在 main 方法中正确返回?苹果文档说返回值是自动返回的。

我假设确实如此。

于 2012-08-08T22:36:58.747 回答