NSInvocationOperation object release - objective-c

I've created NSInvocationOperationQueue object then added it into my NSOperationQueue instance.
operationQueue = [NSOperationQueue new];
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(counterTask) object:nil];
[operationQueue addOperation:operation];
operation = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(colorRotatorTask) object:nil];
[operationQueue addOperation:operation];
It works but I have questions. This post tells me that every operation should be release, but mine without release still works and it gives me error if I release it. If I do not release, is there any side effect or something will happen? Or is there any steps I missed that caused it cannot be released?
Any help would be appreciated. Thank you.

Notice that the project being created in the link you provided doesn't use Automatic Reference Counting (ARC).
There's no reason not to be using ARC in any new project. With ARC you can avoid all of the attempts to call release or autorelease or retain. ARC will properly release the added operation when it is done.
The code you posted in your question is fine with regard to memory management as long as you are using ARC. And since you are getting errors trying to call release this means you are using ARC.

Related

Potential leak IPhone App

HarrisAnnotation *harrisAnnotation = [[HarrisAnnotation alloc] init];
[self.mapAnnotations insertObject:harrisAnnotation atIndex:0];
[harrisAnnotation release];
Running analyze on project show
Potential leak of an object
for
harrisAnnotation
Never mind, I'm not used to using Xcode. It was the line below.

NSOperation subclass release make Instruments crash

I ran instruments on my Mac OS X application in Xcode 4.5. I have two NSOperation dependent subclasses that I forgot to release after adding them in a process queue. So I released them just after adding them in the queue. The application works great. I profiles it on Instruments but it crashes.
processQueue = [[NSOperationQueue alloc] init];
NSUInteger max = [[NSUserDefaults standardUserDefaults] integerForKey:#"jobsKey"];
processQueue.maxConcurrentOperationCount = max;
GeocacheDownloadOperation * downloadOp = [[GeocacheDownloadOperation alloc] initWithGeocache:cache InPath:directoryPath withDelegate:self];
GeocacheJPGConversionOperation * conversionOp = [[GeocacheJPGConversionOperation alloc] initWithCache:cache WithPath:directoryPath WithDelegate:self];
[conversionOp addDependency:downloadOp];
[processQueue addOperation:downloadOp];
[processQueue addOperation:conversionOp];
[downloadOp release];
[conversionOp release]; //This line makes Instruments crash
Instruments crash when I want to release the last operation (see on code) but the application seems to work great.
Did someone have a suggestion ? Is it an Instruments bug or did I code something wrong ?
My guess is that conversionOp releases every dependency (so downloadOp in this case) when deallocated. So you call [conversionOp release] after both operations are finished (and this depends on how the threads are scheduled), so you over-release downloadOp.
I found the error but I can't explain why it was working alone and not in Instruments. I used a released variable in the NSOperation subclasses, that I released the second times in the dealloc function of the NSOperation subclasses. Now, I don't override [super dealloc] anymore in the NSOperationsubclasses and it works.

Excuting fragment of code with dependency in NSOperationQueue rather than invoking methods with NSInvocationOperation

I would like to execute code sequentially in an NSOperation.
This can be easily done by instantiating NSInvocationOperation, for example in a controller of mine I have:
NSOperationQueue *queue = [NSOperationQueue new];
NSInvocationOperation *prepare = [[NSInvocationOperation alloc] initWithTarget:self
selector:#selector(prepare)
object:nil];
NSInvocationOperation *load = [[NSInvocationOperation alloc] initWithTarget:self
selector:#selector(loadData)
object:nil];
[load addDependency:prepare];
This works perfectly, and most of all I can keep dependency between operations. But as you can see I had to create methods encapsulating the operation itself.
I would like instead to instantiate the invocation as a fragment of code, and keep dependency.
Therefore my view controller would be clean for never again used methods.
I saw there is a ^block option but seems that dependency is not maintained.
thanks
You should be able to add dependencies with any subclass of NSOperation. In your case you probably want NSBlockOperation:
NSOperation *prepare = [NSBlockOperation blockOperationWithBlock:^{
// code here
}];
NSOperation *laod = [NSBlockOperation blockOperationWithBlock:^{
// code here
}];
[load addDependency:prepare];

Can I use autorelease to fix this memory leak?

I have a memory leak in my iPhone app. I added Google AdMob to my app using sample code downloaded from Google. However, I was having a hard time getting it into testing mode so I added an extra variable as follows:
GADRequest *r = [[GADRequest alloc] init];
r.testing = YES;
[bannerView_ loadRequest:r];
I found the memory leak using Instruments. Instruments does not lead me to this line of code, it just dumps me in the main.m file. However, when I comment out the code relating to AdMob the leak goes away and I know enough to see that I haven't taken care of releasing this new variable. I just don't know exactly how I should set about releasing it. The variable r is not addressed in the header file so this is all the code that deals with it.
I tried adding:
- (void)dealloc {
[r release];
....
}
but that caused a build error saying "'r' undeclared". That's weird because it looks to me like I'm declaring r in the first quoted line above but I guess that's wrong. Any help would be much appreciated. I really have tried to educate myself about memory leaks but I still find them very confusing.
Just add [r release]; right below the code:
GADRequest *r = [[GADRequest alloc] init];
r.testing = YES;
[bannerView_ loadRequest:r];
[r release];
The variable r is declared only in that section of your code, so that's where it should be released. The point of releasing is to get rid of it as soon as you no longer need it, so the above should work perfectly for you.
If your r is locally declared (as it seems, judging from your snippet), then it cannot be accessed from outside its scope (here: the method it was declared in).
You either need to make it accessible within your class instance by declaring it an ivar.
Declaring it an ivar would look like this:
#interface YourClass : SuperClass {
GADRequest *request;
}
//...
#end
You then change your code to this:
request = [[GADRequest alloc] init];
request.testing = YES;
[bannerView_ loadRequest:request];
Also don't forget to release it in dealloc:
- (void)dealloc {
[request release];
//...
}
However this is not what you want in this situation (I've just included it to clarify why you get the warning about r not being declared).
You (most likely) won't need request any second time after your snippet has run, thus storing it in an ivar will only needlessly occupy RAM and add unwantd complexity to your class. Stuff you only need at the immediate time after its creation should be taken care of (released) accordingly, that is within the very same scope.
What you'll actually want to do is simply (auto)release it, properly taking care of it.
Keep in mind though that your loadRequest: will need to take care of retaining r for as long as it needs it. Apple's implementation does this, of course. But you might want to write a similar method on your own one day, so keep this in mind then.
GADRequest *r = [[GADRequest alloc] init];
r.testing = YES;
[bannerView_ loadRequest:r];
[r release]; //or: [r autorelease];
OP here. Thanks for all the detailed and thoughtful responses. This has definitely helped get a better handle on memory management. I did exactly as recommended, adding [r release]; right below the posted code. However, I still have a leak. I have isolated it to a single line of code. The following leaks:
GADRequest *r = [[GADRequest alloc] init];
r.testing = YES;
[bannerView_ loadRequest:r];
[r release];
The following does not leak:
GADRequest *r = [[GADRequest alloc] init];
r.testing = YES;
// [bannerView_ loadRequest:r];
[r release];
I figure I'm changing the retain count on bannerView with the loadRequest but I don't know how to fix it. I tried a [bannerView_ release] immediately after the [r release]; line (i.e. releasing locally) but that didn't work. I didn't expect it to because bannerView_ is declared elsewhere. I tried [bannerView_ release]; in the dealloc method but that didn't work. I also tried [bannerView_ autorelease]; locally. The wise heads at Google put [bannerView_ release]; in the ViewDidUnload method.
It's possible that Instruments is just messing with my head. The leak appears after about 10 seconds but the app performs well and the amount of memory leaked doesn't seem to be spirally upwards as the app continues to run. Is there such a thing as a benign memory leak?
Thanks again for your help,
Dessie.

releasing NSInvocationOperation causes app to crash

Hi I have the following code
NSString *analyticsStr = [[NSString alloc] initWithString:[self constructXMLMessage:TagObj]];
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self
selector:#selector(sendAnalyticsString:)
object:analyticsStr];
[operationQueue addOperation:operation];
[analyticsStr release];
//[operation release];
when I uncomment [operation release] my app crashes. And I get this error :
malloc: * error for object 0x726ed50: pointer being freed was not allocated
* set a breakpoint in malloc_error_break to debug
I was of view that NSOperationQueue takes care of retaining objects. is there something I am doing wrong or not aware of.
Use Instruments's Zombies template to debug this. A flag will appear in the timeline when you send an object a message after it should have deallocated; you can click the button in that flag to begin investigating what unduly released the object.
By the way, you don't need to create that string object. The string that constructXMLMessage: returns will last as long as the current autorelease pool, which should be all the time you need to work with it. It won't suddenly die on you.