NSInvocation getReturnValue: a call inside forwardInvocation: makes a call to the returned dealloc object:

Here is a separate test.m file that I use to test the behavior.

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 [invocation getReturnValue:&result]; The returned object will not be dealloc ated. I don’t know if this is a mistake, or I just don’t understand how NSInvocation works.

+6
source share
3 answers

The problem is that by default result is __strong , so when it goes out of scope, the compiler creates a release for it. But getReturnValue: did not give you ownership of the returned object, so your method should not issue it.

You can fix this by modifying the result declaration:

 __unsafe_unretained id result; 

This prevents the compiler from creating a release for result when the result out of scope. If you need to save it, you can copy it to another __strong variable.

You can 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 may also report this as an error. I was expecting the compiler, or at least the static analyzer, to warn you that you are converting a pointer to a strong id to a void pointer. http://bugreport.apple.com

+21
source

This happened because ARC cannot manage objects that were written as pointers. Only direct appointment.

Wrong:

 id result; [invocation getReturnValue:&result]; 

On right:

 void *pointer; [invocation getReturnValue:&pointer]; id result = (__bridge id)pointer; //Correct, ARC will retain pointer after assignment 
+4
source
 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 a pointer to the result that is not a dedicated stack or does not call getReturnValue.

The API may suggest that since you called getReturnValue, which you are going to save (and possibly consume the return value). You didn’t. When you delete getReturnValue, is the return value returned in the main method? Apple documents say that the return value is returned automatically.

Suppose he does.

0
source

Source: https://habr.com/ru/post/922422/


All Articles