objective-cmultithreadingnsoperationqueuereference-countingnsinvocationoperation

Arguments retained After finishing NSIvocationOperation?


Edited after I got the answer:
- Actually there are no memory leaks. Object added to NSInvocationOperation will be retained and also released as expected.

Why is it that after I add an object to NSInvocationOperation it is retained and also after that operation is finished it is retained once again?

And how do I prevent memory leaks?

Down there is my example code. I apologize if that much code is annoying you, but I just wanted to be sure that I didn't miss anything. Also, comments next to NSLogs show their output.

My whole AppDelegate.m:

//
//  AppDelegate.m
//  BRISI
//
//  Created by Aleksa Topic on 2/22/12.
//  Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//

#import "AppDelegate.h"

@implementation AppDelegate

@synthesize window = _window;

- (void)dealloc
{
  [_window release];
  [super dealloc];
}

- (BOOL)application:(UIApplication *)application
        didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  NSObject *a = [[NSObject alloc] init];
  NSLog(@"a1: %d", a.retainCount); // a1: 1

  NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
  NSInvocationOperation *operation = [[NSInvocationOperation alloc]
                                      initWithTarget:self
                                            selector:@selector(s:)
                                              object:a];
  NSLog(@"a2: %d", a.retainCount); // a2: 2

  [a release];
  NSLog(@"a3: %d", a.retainCount); // a3: 1

  [operationQueue addOperation:operation];
  [operation release];
  NSLog(@"a4: %d", a.retainCount); // a4: 1

  NSLog(@"oper1: %@", operation); // oper1: <NSInvocationOperation: 0x6a3f7d0>

  NSLog(@"a5: %d", a.retainCount); // a5: 1
  NSLog(@"a5: %d", a.retainCount); // a5: 1
  NSLog(@"a5: %d", a.retainCount); // a5: 1
  NSLog(@"a5: %d", a.retainCount); // a5: 2
  NSLog(@"a5: %d", a.retainCount); // a5: 2
  NSLog(@"a5: %d", a.retainCount); // a5: 2
  NSLog(@"a5: %d", a.retainCount); // a5: 2
  NSLog(@"a5: %d", a.retainCount); // a5: 2

  // And here I get: "Thread 1: Program received signal: "EXC_BADACCESS"."
  NSLog(@"oper2: %@", operation);

  self.window = [[[UIWindow alloc]
                  initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
  // Override point for customization after application launch.
  self.window.backgroundColor = [UIColor whiteColor];
  [self.window makeKeyAndVisible];
  return YES;
}

- (void)s:(NSArray *)a
{
  NSLog(@"a (s:): %d", a.retainCount); // a (s:): 1
  NSLog(@"a (s:): %d", a.retainCount); // a (s:): 1
  NSLog(@"a (s:): %d", a.retainCount); // a (s:): 1
}

@end

And here is output in case you want to compare:

GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "x86_64-apple-darwin".sharedlibrary apply-load-rules all
Attaching to process 7927.
2012-03-06 16:19:20.712 BRISI[7927:207] a1: 1
2012-03-06 16:19:20.715 BRISI[7927:207] a2: 2
2012-03-06 16:19:20.715 BRISI[7927:207] a3: 1
2012-03-06 16:19:20.716 BRISI[7927:207] a4: 1
2012-03-06 16:19:20.717 BRISI[7927:207] oper1: <NSInvocationOperation: 0x6858da0>
2012-03-06 16:19:20.717 BRISI[7927:1e03] a (s:): 1
2012-03-06 16:19:20.717 BRISI[7927:207] a5: 1
2012-03-06 16:19:20.718 BRISI[7927:1e03] a (s:): 1
2012-03-06 16:19:20.718 BRISI[7927:207] a5: 1
2012-03-06 16:19:20.718 BRISI[7927:1e03] a (s:): 1
2012-03-06 16:19:20.719 BRISI[7927:207] a5: 1
2012-03-06 16:19:20.719 BRISI[7927:207] a5: 2
2012-03-06 16:19:20.720 BRISI[7927:207] a5: 2
2012-03-06 16:19:20.720 BRISI[7927:207] a5: 2
2012-03-06 16:19:20.721 BRISI[7927:207] a5: 2
2012-03-06 16:19:20.721 BRISI[7927:207] a5: 2
Current language:  auto; currently objective-c
(gdb) 

Solution

  • Whenever I am dealing with a tricky retain/release set of operations I make sure I override the appropriate methods:

    @interface RetainCountChecker : NSObject
    
    @end
    
    @implementation RetainCountChecker
    
    -(oneway void)release{
        [super release];
        NSLog(@"release - retainCount = %d", [self retainCount]);
    }
    
    -(id)retain{
        id result = [super retain];
    
        NSLog(@"retain - retainCount = %d", [self retainCount]);
        return result;
    }
    
    -(void)dealloc{
        [super dealloc];
    }
    
    @end
    

    Putting breakpoints in the retain, release, and dealloc methods allows you to see when and how each of those are being called. In your case the NSInvocationOperation is autoreleasing the internal NSInvocation object. Wrapping the code in question in an

    @autorelease{
    
    }
    

    block will dealloc "a" before the -application:didFinishLaunchingWithOptions: method returns.