问题
Here's a standalone test.m
file that I'm using to test the behavior.
To compile: clang test.m -o test.app -fobjc-arc -ObjC -framework Foundation
. Make sure the Xcode command-line tools are installed.
#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;
}
If I comment out [invocation getReturnValue:&result];
, the returned object isn't dealloc
ated. I don't know if this is a bug, or just me misunderstanding how NSInvocation
works.
回答1:
The problem is that result
is __strong
by default, so when it goes out of scope, the compiler generates a release
for it. But getReturnValue:
didn't give you ownership of the returned object, so your method shouldn't be releasing it.
You can fix this by changing the declaration of result
:
__unsafe_unretained id result;
This prevents the compiler from generating a release
for result
when result
goes out of scope. If you need to retain it, you can copy it to another, __strong
variable.
You could also add a category to NSInvocation
to handle this for you:
@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];
}
...
You could also report this as a bug. I would expect the compiler, or at least the static analyzer, to warn you that you're converting a pointer to a strong id
to a void pointer. http://bugreport.apple.com
回答2:
It was because ARC cannot manage objects which was written as pointers. Only directly assignment.
Wrong:
id result;
[invocation getReturnValue:&result];
Right:
void *pointer;
[invocation getReturnValue:&pointer];
id result = (__bridge id)pointer; //Correct, ARC will retain pointer after assignment
回答3:
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
You must set the result to to a pointer which is not stack allocated or not call getReturnValue.
The API may assume that since you called getReturnValue that you are going to retain (and possibly consume the return value). You didnt. When you remove getReturnValue does the return value come back properly in the main method? The apple docs say that the return value is returned automatically.
Im assuming it does.
来源:https://stackoverflow.com/questions/11874056/nsinvocation-getreturnvalue-called-inside-forwardinvocation-makes-the-returned