Retain Count & Copy In Setter? - objective-c

This is a followup question from a previous question, which is hopefully a little clearer. I am just curious how the code presented below is working, specifically is the variable myString getting released. It does not look like it is from the output?
CODE
// IMPLEMENT
#implementation CelestialBody
- (void)setName:(NSString *)newName{
if(name != newName) {
[name release];
name = [newName copy];
}
}
- (void)dealloc{
[name release];
name = nil;
[super dealloc];
}
#end
.
// ------------------------------------------------------------------- **
// MAIN: 30th September 2009
// ------------------------------------------------------------------- **
#import <Foundation/Foundation.h>
#import "CelestialBody.h"
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
CelestialBody *newPlanet = [[CelestialBody alloc] init];
NSString *myString = [[NSString alloc]initWithFormat:#"go home"];
NSLog(#"RetainCount_1: %d",[myString retainCount]);
[newPlanet setName: myString];
NSLog(#"RetainCount_2: Incremented by copy in setName");
// Clean up
NSLog(#"RetainCount_2: %d -Before Release",[myString retainCount]);
[newPlanet release];
[myString release];
[pool drain];
NSLog(#"RetainCount_1: %d -After Release",[myString retainCount]);
return 0;
}
// ------------------------------------------------------------------- **
OUTPUT
Running…
2009-10-01 09:28:50.395 RetainCount_1: 1
2009-10-01 09:28:50.399 RetainCount_2: Incremented by copy in setName
2009-10-01 09:28:50.399 RetainCount_2: 2 -Before Release
2009-10-01 09:28:50.400 RetainCount_1: 1 -After Release
Debugger stopped.
I am currently re-reading the Memeory Management Guide to try and see what I have missed.
many thanks
EDIT
Just added a the release to the dealloc, It looks like that was what I was missing.
- (void)dealloc{
[name release];
name = nil;
[super dealloc];
}
gary

is the variable myString getting released.
[myString release];
All signs point to yes.
It does not look like it is from the output?
NSLog(#"RetainCount_2: %d",[myString retainCount]);
[myString release];
Your NSLog statement's output doesn't reflect the release message because the release message hasn't happened yet.
Also, don't worry about retain counts. They can be very misleading. As long as you follow Cocoa's rules and don't create any ownership cycles (A owns B owns C owns A), you'll rarely have a problem.

This is not an answer to your question, per se, but an explanation of what you're seeing: The last call to retainCount is sent to a deallocated object, which is undefined behavior. The object happens not to have been overwritten yet, so it still kindasorta "works" in the sense that the method dispatch can still see the old data that's there and doesn't realize it's invalid. You will never get back 0 from calling retainCount, because such an object can't exist.

I guess [newName copy] doesn't actually copy the NSString, because NSString is immutable? I never thought about this, but it makes sense to me.
At the end of your program you're releasing newPlanet and myString. My question is, are you releasing the instance variable name in the -dealloc method of CelestialBody? If you're not, then I believe you're leaking memory there.
Create myString: the NSString's retainCount is 1
[newPlanet setName:]: the NSString's retainCount is 2
[newPlanet release]: the NSString's retainCount is 2 ? I guess you're not releasing it in -dealloc.
[myString release]: the NSString's retainCount is 1

Related

Problems with subclassing NSString

I have spent the last day hunting down a dynamic storage issue and at the end of the trail I have no idea what is going on other than I must have misunderstood/missed something about subclassing NSString. Here is a much cut down and much instrumented sample that has the problem:
IDStringBug.h contains:
#import <Foundation/NSArray.h>
#import <Foundation/NSString.h>
/*==================================*/
#interface IDStringBug:NSString {
NSString *_backingStore;
NSArray *path;
}
- (NSArray*) path;
- (void) dealloc;
- (NSUInteger) length;
-(id) initWithString: (NSString*) string;
-(unichar) characterAtIndex:(NSUInteger) index;
#end
IDStringBug.m contains:
#include <stdio.h>
#import "IDStringBug.h"
#implementation IDStringBug
- (NSArray*) path {
printf ("Return ptr to IDString: %s\n", [_backingStore cString]);
return path;}
- (void) dealloc {
printf ("Release IDString: %s\n", [_backingStore cString]);
printf ("Path count is %d\n", (int) [path retainCount]);
[_backingStore release];
printf ("Apres _backinstore\n");
printf ("Path count is %d\n", (int) [path retainCount]);
[path release];
printf ("After path release, done but for super\n");
[super dealloc];
}
-(id)initWithString:(NSString*)string {
if ((self = [self init])) {
_backingStore = [[NSString stringWithString:string] copy];
}
path = [_backingStore componentsSeparatedByString: #"."];
printf ("Path count is %d\n", (int) [path retainCount]);
return self;
}
-(NSUInteger) length {
return [_backingStore length];
}
-(unichar)characterAtIndex:(NSUInteger)index {
return [_backingStore characterAtIndex:index];
}
#end
bug.m contains:
#include <stdio.h>
#include <Foundation/NSAutoreleasePool.h>
#import "IDStringBug.h"
int main(int argc, char* argv[]) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
IDStringBug *myids = [IDStringBug stringWithString: #"a.b.c"];
printf ("Path count is %d\n", (int) [[myids path] retainCount]);
printf ("pool=%d\n", (int) [pool autoreleaseCount]);
[pool release];
}
The output is:
$ ./bug
Path count is 1
Return ptr to IDString: a.b.c
Path count is 1
pool=7
Release IDString: a.b.c
Segmentation fault (core dumped)
This answer doesn't directly address your problem, but it will indirectly fix it and lead to a much more maintainable design pattern.
Don't subclass NSString.
Instead, use composition.
#interface PathString:NSObject
#property(copy) NSString *stringValue;
#property(strong) NSArray *pathValue;
... etc ...
#end
The actual crash is this:
path = [_backingStore componentsSeparatedByString: #"."];
That method returns an autoreleased object and it is deallocated when the pool is drained, leaving a dangling reference.
As others have mentioned, retainCount is utterly useless.
Note that this is odd:
_backingStore = [[NSString stringWithString:string] copy];
That should simply be:
_backingStore = [string copy];
Your code is technically copying the string twice. I say technically, because -- due to an implementation detail -- _backingStore will end up pointing to string (assuming string is an NSString and not an NSMutableString.
I was very much an insider back in the NeXT days, but have been mostly
away or only used Objc base.
Aha! So was I, having started ObjC programming in 1989.
That'd explain where you are coming from a bit!
Instead of retainCount, an issue like this is quite easy to debug using zombies. You can turn it on in the options pane of the scheme in Xcode.
The "whentouseretaincount.com" site links to an article I wrote about retain count. You might find it interesting in that it also illuminates some details of memory management, in general.
Apple document to the Linux GnuStep world
That is also critical to note in your questions. GNUStep is mostly just like Apple stuff, but is a little closer to the OpenStep world. I can't remember if GNUStep has zombie detection, but I'd suspect it would. Linux also has other memory debugging tools that are quite powerful.
retainCount is still going to be rife with fragility, but it is somewhat more stable when dealing with a single threaded, command line, tool. You'll still need to watch out for autoreleased stuff, though.
The call to componentsSeparatedByString: returns an NSArray that has been autoreleased. That means it will have a retain count of 1, but that count will be decremented as soon as the autorelease pool is drained. Combine that with the release call in the IDStringBug dealloc, and you'll see that the array is being released one time too many.
In fact by the time the IDStringBug dealloc is called, the path array has already been deallocated. So when you try to determine the retain count (with the call to [path retainCount] you are attempting to access an object that no longer exists.

calling retain, retainCount analyzing EXC_BAD_ACCESS with release

I'm a java programmer new to Objective C, so please be gentle :)
I'getting an errorr message saying EXC_BAD_ACCESS on calling release on an object:
I read documentation and threads on this site, but I see data that's confusing me
- (void) dealloc {
NSLog(#"dealloc in image Retain count: %i", [image retainCount]);
[image release];//method throwing EXC_BAD_ACCESS
..............
}
the logged retain count is: 1
In the code that is causing the dealloc I have:
UIImage *scrn = [[UIImage alloc] initWithCGImage:newImage];
NSLog(#"in after instantiation Retain count: %i", [scrn retainCount]);// logs retain count of 1
CGImageRelease(newImage);
Decoder *d = [[Decoder alloc] init];
.....
NSLog(#"in before decoding Retain count: %i", [scrn retainCount]);// logs retain count of 1
decoding = [d decodeImage:scrn cropRect:cropRect] == YES ? NO : YES;
NSLog(#"in after decoding Retain count: %i", [scrn retainCount]); // logs retain count of 2
[d release]; // this line causes invocation of dealloc on the previous code sniplet
[scrn release];
In decodeImage the following is going on:
- (BOOL) decodeImage:(UIImage *)i cropRect:(CGRect)cr {
NSLog(#"Decoder.mm.decodeImage initial Retain count i : %i retaincount image %i", [i retainCount], [image retainCount]); //logs: Decoder.mm.decodeImage initial Retain count i : 1 retaincount image 0
[self setImage: i];
NSLog(#"Decoder.mm.decodeImage after setting image Retain count i : %i retaincount image %i", [i retainCount], [image retainCount]);//logs: Decoder.mm.decodeImage after setting image Retain count i : 2 retaincount image 2
.......
return [self decode];
}
There are several things puzzeling me:
From what understood the retainCount is increased by calling retain or by instantiating a new object, not by assigningment of one var to another as is done in self setImage: i]; however i see that the retaincount is increased by one
Before calling [d release] the logged retainCount is 2, in the method dealloc the count is 1
If the count is 1, why do I get the EXC_BAD_ACCESS ???
Edit: added additional code as requested
#implementation Decoder
#synthesize image;
The setting of image is mentioned in the third code sniplet above.
Retain count can also be incremented by the system (for build-in types) and by properties that are defined with the retain or copy attributes. You're only responsible for the ones you cause (not the system retains), but don't depend on the retain count when trying to determine why you're getting EXC_BAD_ACCESS. XCode has some good build-in analysis tools that are better for tracking down access errors.
One important thing to note: your retaincount will never go below 1 even if you release when the count is 1.
See this question for good information on retaincount.

Why does my object still work after countless releases?

I can never seem to deallocate my NSMutableString as shown below. The initial retain count should be 1, but after releasing several times, the string is still usable like nothing happened!
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// insert code here...
NSMutableString* s = [[NSString alloc]initWithString:#"AAA"];
[s release];
[s release];
[s release];
[s release];
[s release];
NSLog(#"%#",s);
[pool drain];
return 0;
}
Of course, if I use Analyze it still tells me that I release a released object on the second release.
Scott's answer is the correct general one, but in this particular case, the reason is that NSString literals (i.e. #"") are uniqued compile-time constants and do not actually do anything at all when retained and released. Your assignment of it to an NSMutableString* does not actually make it an NSMutableString, so what you've written is equivalent to
[#"AAA" release];
[#"AAA" release];
[#"AAA" release];
[#"AAA" release];
[#"AAA" release];
[#"AAA" release];
Releasing an object tells the runtime that it can destroy the object, at least as far as you're concerned, but it doesn't require that the object be destroyed immediately: After your first [s release], Cocoa is free to do whatever it pleases with the memory formerly used by s. It might give that memory to the next object that does an alloc, in which case your later attempts to access s will result in a fiery runtime crash… or it might not need that memory right away, in which case you might get away with accessing a released object.
The rule of thumb is less "I've released this object, which means it no longer exists" and more "I've released this object, which means it's no longer guaranteed to exist."

NSArray acts weirdly with objects going out of scope

I have a weird problems with NSArray where some of the members of the objects in my array are going out of scope but not the others:
I have a simple object called Section.
It has 3 members.
#interface Section : NSObject {
NSNumber *section_Id;
NSNumber *routeId;
NSString *startLocationName;
}
#property(nonatomic,retain) NSNumber *section_Id;
#property(nonatomic,retain) NSNumber *routeId;
#property(nonatomic,retain) NSString *startLocationName;
#end
#implementation Section
#synthesize section_Id;
#synthesize routeId;
#synthesize startLocationName;
//Some static finder methods to get list of Sections from the db
+ (NSMutableArray *) findAllSections:{
- (void)dealloc {
[section_Id release];
[routeId release];
[startLocationName release];
[super dealloc];
}
#end
I fill it from a database in a method called findAllSection
self.sections = [Section findAllSections];
In find all sections I create some local variables fill them with data from db.
NSNumber *secId = [NSNumber numberWithInt:id_section];
NSNumber *rteId = [NSNumber numberWithInt:id_route];
NSString *startName = #"";
Then create a new Section and store these local variable's data in the Section
Section *section = [[Section alloc] init];
section.section_Id = secId;
section.routeId = rteId;
section.startLocationName = startName;
Then I add the section to the array
[sectionsArray addObject:section];
Then I clean up, releasing local variables and the section I added to the array
[secId release];
[rteId release];
[startName release];
[locEnd_name release];
[section release];
In a loop repeat for all Sections (release local variables and section is done in every loop)
The method returns and I check the array and all the Sections are there. I cant seem to dig further down to see the values of the Section objects in the array (is this possible)
Later I try and retrieve one of the Sections
I get it from the array
Section * section = [self.sections objectAtIndex:row];
Then check the value
NSLog(#" SECTION SELECTED:%#",section.section_Id);
But the call to section.section_Id crashed as section.section_Id is out of scope.
I check the other members of this Section object and they're ok.
After some trial and error I find that by commenting out the release of the member variable the object is OK.
//[secId release];
[rteId release];
[startName release];
[locEnd_name release];
[section release];
My questions are:
Am I cleaning up okay?
Should I release the object added to an array and the local variable in the function?
Is my dealloc okay in Section?
Does this code look ok and should I be looking elsewhere for the problem?
I'm not doing anything complicated just filling array from DB use it in Table Cell.
I can comment out the release but would prefer to know why this works, and if I shouldn't be doing this. The only place that secId is released is in the dealloc.
You should not be releasing secId, rteId, or startName. secId and rteId are pointers to NSNumber instances created with a factory method that returns an already-autoreleased object. Static strings (i.e. #"") do not need to be released. You need to re-read the Memory Management Programming Guide. Then read it again ;-) It will be your friend.
You're releasing objects you don't own. You should read the memory management rules.
I'll second (third) the suggestion to read the memory management rules.
The TL;DR version is anything you alloc and call a method with init in the method name on is your responsibility to release. For instance:
NSString *string = [[NSString alloc] initWithFormat:#"%#", someObject];
In this case you must release string. However:
NSString *string = [NSString stringWithFormat:#"%#", someObject];
Here string is autoreleased. It's basically equivalent to this:
NSString *string = [[[NSString alloc] initWithFormat#"%#", someObject] autorelease];
...meaning that the next time through the event loop (which means possibly as soon as your function returns), the system will send a release message to it for you. Apple calls these "convenience methods".
If you have something like this:
NSString *string = #"foo";
Then string is pointing to an instance of NSString that is created by the runtime when your program initializes and won't go out of scope until your program terminates. Never release these either.
Again, read the guidelines and bookmark them. But this should answer your direct question.

Releasing local variables before return?

In objective-c, I understand that you need to release anything you init/retain/copy. Do I need to do that before a return statement? I'm wanting to understand calling release explicitly and not use autorelease.
-(void) someMethod
{
AnotherClass* ac = [[AnotherClass alloc] init];
if([ac somethingHappens]){
// Do I need to release ac here?
return;
}
[ac doSomethingElse];
[ac release];
}
Thanks!
Yes, you need to release your variables, however you exit from the method.
It's pretty straight-forward: when you init something the retain count is incremented. When you release it's decremented. When it reaches zero it's automatically deallocated (freed).
In your code above, you init the variable but if it follows the return route then the variables retain count never gets to zero and, therefore, is never deallocated.
Suppose to have a local variable assigned like the following
NSString *placeHolder = [NSString stringWithFormat:#"%# %#",[someObject value1], [someObject value2]];
Now pass this variable to a object defined method, such as setPlaceholder of UISearchBar object
[self.theSearchBar setPlaceholder:placeHolder];
How to release in the right way the assigned string 'placeHolder' ?
If you suppose to autoreleas it:
NSString *placeHolder = [[NSString stringWithFormat:#"%# %#",[someObject value1], [someObject value2]] autorelease];
your code will fail with a bad_exc_access
If you think to release the variable after passed to somewhere else like
[self.theSearchBar setPlaceholder:placeHolder];
[placeHolder release];
a runtime exception will throw too.
So what's wrong?
The problem is the retain count. The UISearchBar object is allocated yet, so if you release or auto-release such a variable, referred by that object, the retain count is still the same
NSLog(#"Retain count before assign to refer other object %d", [placeHolder retainCount]);
[self.theSearchBar setPlaceholder:placeHolder];
NSLog(#"Retain count after referencing %d", [placeHolder retainCount]);
So, how to handle this?
Try something like the following
[placeHolder retain]; // retainCount +1
[self.theSearchBar setPlaceholder:placeHolder];
[placeHolder release]; // retainCount -1
What we did than ? Let's have a look at the retain count now
NSLog(#"Retain count before doing retain %d", [placeHolder retainCount]);
[placeHolder retain]; // retainCount +1
NSLog(#"Retain count after retaining it %d", [placeHolder retainCount]);
So, we incremented the retain count before assign it (get referenced by) to some object, and - after that - we release locally that variable.
That's all.