Potential leak check in iPhone sdk - objective-c

NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
//responseData = nil;
NSMutableDictionary* json =[[responseString JSONValue] retain];
[responseString release];
NSLog(#"Sample message %d",[json retainCount]);
Here m getting retain count is 2, can any one help in this how can i track, this retain count.
The problem is when i write the following code the app is crashing.
[json release];
app crashes.

retainCount is useless. Don't call it.
You can't use retainCount to try and determine when something needs to be released.
In that code, it isn't clear why it is crashing. Post the backtrace of the crash.

Related

NSArray release crashes app

I am trying to "tokenize" my data that I get from my text file.
When I am doing this, I get an error like this:
malloc: * error for object 0x844c730: pointer being freed was not allocated
* set a breakpoint in malloc_error_break to debug
The code I use looks like this:
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"mydata" ofType:#"txt"];
NSString *rawText = [NSString stringWithContentsOfFile:filePath encoding:NSASCIIStringEncoding error:nil];
//No error was caused by above line
NSArray *tmp = [rawText componentsSeparatedByString:#"####"];
NSString *title = #"";
NSString *detail = #"";
for(int i = 0; i < [tmp count]-1; i++)
{
NSArray *base = [[tmp objectAtIndex:i] componentsSeparatedByString:#"##"];
title = [[NSString alloc] initWithFormat:#"%#$$%#",title,[base objectAtIndex:0]];
detail = [[NSString alloc] initWithFormat:#"%#$$%# | %# | %#",
title,
[base objectAtIndex:0],
[base objectAtIndex:1],
[base objectAtIndex:2]
];
[base release];
}
[tmp release];
It must be this part of the code, since if I comment this piece out, it works fine.
Reading the error it says set a breakpoint which I have no idea to put that in malloc_error_break
What is wrong in my memory management doing?
Or else how can I split up the string in some other way?
You got tmp from componentsSeparatedByString:. Since that selector doesn't start with "alloc" or "new" or "copy" or "mutableCopy", and since you didn't do [tmp retain], you don't own tmp. So you shouldn't do [tmp release].
Same for base.
Base and temp are autorelease objects so you should not release that objects.
You don't have to release base. It's already autoreleased.
You are trying to release array base without allocating it.
NSArray *base = [[tmp objectAtIndex:i] componentsSeparatedByString:#"##"]; didn't allocate memory for base.You don't need [base release];
until it is allocated.

Weird crash if I try to release CXMLDocument

I am parsing some XML using TouchXML and I am getting a crash -EXC_BAD_ACCESS. What I found out through trial and error was that if I don't release my CXMLDocument (which I allocate), then everything is fine. Here's my code:
- (NSArray *)getLookUps {
//Do some stuff and then...
NSData *tempData = [NSURLConnection sendSynchronousRequest:request
returningResponse:nil
error:nil];
CXMLDocument *xmlDoc = [[CXMLDocument alloc] initWithData:tempData options:0 error:nil];
NSDictionary *mappings = [NSDictionary dictionaryWithObject:#"http://****/****"
forKey:#"****"];
NSLog(#"%#", [[NSString alloc] initWithData:tempData encoding:NSUTF8StringEncoding]);
NSArray *orders = [[xmlDoc rootElement] nodesForXPath:#"//****:Unit"
namespaceMappings:mappings
error:nil];
NSMutableArray *units = [NSMutableArray arrayWithCapacity:200];
for (CXMLElement *order in orders) {
NSArray *nodes = [order children];
NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithCapacity:[nodes count]];
for (CXMLElement *node in nodes) {
[dictionary setObject:[node stringValue] forKey:[node name]];
}
[units addObject:dictionary];
}
//[xmlDoc release];
return units;
}
See on the 2nd last line, [xmlDoc release]. I have commented that out, because it crashes if I don't. What am I doing wrong? Thanks.
You probably need to retain your dictionary object otherwise it will also be released when you release the parser. Try changing [units addObject:dictionary]; to [units addObject:[dictionary retain]];.
Another idea is to set your xmlDoc pointer to autorelease:
CXMLDocument *xmlDoc = [[[CXMLDocument alloc] initWithData:tempData options:0 error:nil] autorelease];
This bug was reported and is flagged as fixed in the newer versions of the library.
http://code.google.com/p/touchcode/issues/detail?id=35
I haven't tested to see if it is actually fixed, a comment at that URL suggests that it isn't.
In my opinion, this library should be avoided altogether. For iOS apps, use libxml2 for several reasons:
It's tested and tried, through and through
It's fast and efficient
Building a node based representation of your XML might make it easier to code with, but it wastes memory as you always have the entire document in memory. You probably have it more than once while parsing. You should instead design your code to work with the libxml2 approach. You'll agree once you start parsing documents of substantial size.
I used TouchXML quite often, and (fortunately?) I did not have this problem up to now, but it just happened ...
I posted a solution here:
Memory crash using [CXMLNode nodesForXPath] with namespace mappings
I observed in TouchXML Class "CXMLDocument" we have the following handling in "dealloc" method.
- (void)dealloc
{
// Fix for #35 http://code.google.com/p/touchcode/issues/detail?id=35 -- clear up the node objects first (inside a pool so I _know_ they're cleared) and then freeing the document
#autoreleasepool {
nodePool = NULL;
}
//
xmlUnlinkNode(_node);
xmlFreeDoc((xmlDocPtr)_node);
_node = NULL;
}
I am not sure why we are using "autoreleasepool" in "dealloc". Is this is standard coding? Correct me if I am wrong.

NSString EXC_BAD_ACCESS

I'm stuck with the following bit of code.
NSString *gridRef = [[NSString alloc] initWithFormat: #"%#", [converter LatLongToOSGrid: latLong]];
NSLog(#"Grid Ref: %#", gridRef);
self.answerLabel.text = [[NSString alloc] initWithFormat: #"%#", gridRef];
When I log gridRef, it displays the correct result. However, the line setting answerLabel.text causes an EXC_BAD_ACCESS error and the program crashes. IB is connected to the correct label, what is the problem?
Thanks
I've updated the code as follows:
- (IBAction)convertLatLong {
NSArray *latLong = [[NSArray alloc] initWithObjects: latTextField.text, longTextField.text, nil];
GridRefsConverter *converter = [[GridRefsConverter alloc] init];
NSString *gridRef = [[NSString alloc] initWithFormat: #"%#", [converter LatLongToOSGrid: latLong]];
NSLog(#"Grid Ref: %#", gridRef);
NSLog(#"Label: %#", self.answerLabel.text);
answerLabel.text = #"Yippy";
self.answerLabel.text = gridRef;
[gridRef release];
[converter release];
[latLong release];
}
answerLabel is initialised through #property #synthesize when the view controller is pushed onto the stack. (I don't know how it gets init'd apart from it's one of the magical things IB does for you. Or so I assume. I've used exactly the same method in other view controllers and have not had this issue.
I've found the culprits - the question is, how do I go about releasing them?
NSString *eString = [[NSString alloc] initWithFormat: #"%f", e];
NSString *nString = [[NSString alloc] initWithFormat: #"%f", n];
eString = [eString stringByPaddingToLength: (digits/2) withString: #"0" startingAtIndex: 0];
nString = [nString stringByPaddingToLength: (digits/2) withString: #"0" startingAtIndex: 0];
NSString *theGridRef = [letterPair stringByAppendingString: eString];
theGridRef = [theGridRef stringByAppendingString: nString];
[eString release];
[nString release];
return theGridRef;
and:
NSArray *gridRef = [[NSArray alloc] init];
gridRef = [gridRef arrayByAddingObject: [NSNumber numberWithDouble: E]];
gridRef = [gridRef arrayByAddingObject: [NSNumber numberWithDouble: N]];
gridRef = [gridRef arrayByAddingObject: [NSNumber numberWithInteger: 8]];
NSString *theGridRef = [[NSString alloc] initWithFormat: #"%#", [self gridRefNumberToLetter: gridRef]];
[gridRef release];
[theGridRef autorelease];
return theGridRef;
}
You should enable zombie detection by setting the environment variable NSZombieEnabled to YES, so you can see which object causes the bad access (don't forget to remove this again when you found the bug).
Also you can use Instruments to find the location where the object actually gets released. For this start a new Instruments session and use the "Allocations" instrument. In the instrument settings check "Enable NSZombie detection" and "Record reference counts". When running the session you will break where the error occurs and you see a record of all retains/releases.
One place where you can have a quick look if your object is incorrectly freed is in the -viewDidUnload method, where you should release the outlet and set it to nil. If you forget the latter and you access the outlet somehow, it will result in a EXC_BAD_ACCESS.
Edited to match your update:
The problem is that you are assigning eString (and nString) a new string which was alloc/init-ed. Then you override those in the next statements, because -stringByPaddingToLength: (as well as all the other -stringBy... methods) return a new and autoreleased string object. So you lost the reference to the old string which means that there is a memory leak. Additionally at the end you release the already autoreleased objects explicitly which causes your bad access.
Instead you should create autoreleased strings from the beginning ([NSString stringWithFormat:...]) and don't release them at the end.
Check if asnwerLabel is actually non-null. You should also change this line:
self.answerLabel.text = [[NSString alloc] initWithFormat: #"%#", gridRef];
To:
self.answerLabel.text = [NSString stringWithFormat: #"%#", gridRef];
Otherwise, you will end up with a memory leak in that line.
Maybe the label is not inited at that point in your code, try to check it. Why are you allocating a new NSString?
Just do:
self.label.text = gridRef;
[gridRef release];
how is answerLabel created? You might need to retain that. Or you possibly need to release some things (gridRef)?
I can't see any other issues with your code.
You can (and probably should) set your
answerLabel.text = gridRef;
gridRef is already an NSString, so you don't need to alloc it again.
EXC_BAD_ACCESS is usually a memory thing related to your retain/release count not balancing (or in my extensive experience of it :p).
Okay, the problem was trying to release NSStrings, so I've stopped doing that and the problem has been solved.
Can someone clarify how strings are retained and released. I was of the impression that:
string = #"My String"; is autoreleased.
NSString *string = [[NSString alloc] init...]; is not autoreleased and needs to be done manually.

NSURLCache crashes with autoreleased objects, but leaks otherwise

CSURLCache is designed to cache resources for offline browsing, as NSURLCache only stores data in-memory.
If cachedResponse is autoreleased before returning the application crashes, if not, the objects are simply leaked.
Any light that could be shed onto this would be much appreciated.
Please note stringByEncodingURLEntities is a category method on NSString.
#interface CSURLCache : NSURLCache {} #end
#implementation CSURLCache
- (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request
{
NSString *path = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:[[[request URL] absoluteString] stringByEncodingURLEntities]];
if ([[NSFileManager defaultManager] fileExistsAtPath:path])
{
NSData *data = [[NSData alloc] initWithContentsOfFile:path];
NSURLResponse *response = [[NSURLResponse alloc] initWithURL:[request URL]
MIMEType:nil
expectedContentLength:[data length]
textEncodingName:nil];
NSCachedURLResponse *cachedResponse = [[NSCachedURLResponse alloc] initWithResponse:response
data:data];
[response release];
[data release];
return cachedResponse;
}
return nil;
}
#end
UPDATE: After submitting a radar to Apple it appears that this is a known issue (Radar #7640470).
- (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request
Well, this isn't an alloc, new, or copy method…
… and CSURLCache doesn't hold on to the object anywhere, so it's not owning it.
So, you need to autorelease it.
Of course, that means the object is doomed unless something retains it. Your app crashed because it tried to use the object after the object died.
Run your app under Instruments with the Zombies template. Look at where the app crashes and what it was doing when cachedResponseForRequest: was called. The caller needs to own the object until the time when the application would crash otherwise, and then release it.

why shouldn't I release this string?

Look at the following method:
-(void)updateProfile:(Profile *)profile WithJSON:(NSString *)JSON;
{
SBJSON *parser = [[SBJSON alloc] init];
NSDictionary *object = [parser objectWithString:JSON error:nil];
NSNumberFormatter *nf = [[NSNumberFormatter alloc] init];
[nf setPositiveFormat:#"#,##0"];
profile.displayName = [object valueForKey:#"displayName"];
profile.profileURL = [object valueForKey:#"profileURL"];
NSString *rep = [object valueForKey:#"reputation"];
profile.reputation = [[nf numberFromString:rep] intValue];
//[rep release]; <-Why not release?
[nf release];
//[object release]; <-Why not release?
[parser release];
}
I have commented out two lines, which gives me EXC_BAD_ACCESS if not.
Can someone explain to me why it's wrong to release these objects?
You shouldn't release it because you didn't +alloc, -retain, or -copy it. Convenience constructors like +objectWith… return autoreleased objects.
The better question to ask is: Why should you release it? What have you done to claim ownership over the object? The answer in this case is "nothing." Since you don't own it, you can't very well release it.