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.
Related
I have a ViewController defined as follows:
#interface SectionController : UITableViewController {
NSMutableArray *sections;
}
- (void) LoadSections;
When LoadSection is call it makes a call to NSURLConnection to load a url which in turn calls
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
[connection release];
[responseData release];
NSDictionary *results = [responseString JSONValue];
NSMutableArray *jSections = [results objectForKey:#"Items"];
sections = [NSMutableArray array];
for (NSArray* jSection in jSections)
{
Section* section = [Section alloc];
section.Id = [jSection objectForKey:#"Id"];
section.Description = [jSection objectForKey:#"Description"];
section.Image = [jSection objectForKey:#"Image"];
section.Parent = [jSection objectForKey:#"Parent"];
section.ProductCount = [jSection objectForKey:#"ProductCount"];
[sections addObject:section];
[section release];
}
[jSections release];
[results release];
[delegate sectionsLoaded];
[self.view reloadData];
}
The data parses correctly and I now have sections filled with many items.
Calling [self.view reloadData] forces a callback to the delegate method cellForRowAtIndexPath which should then present the data into the cell however its at this point that sections is now nil again.
Can someone please point out my mistake? I must admit I am a newbie to objective c and it probably a pointer issue. What is need to do is retain the value of sections after calling reloadData.
Many thanks.
Seeing the new code the problem is obvious:
sections = [NSMutableArray array];
should become
[sections release];
sections = [[NSMutableArray alloc] init];
note that the array does not become again "nil", is instead deallocated and you get an invalid reference, which might (should) generate a crash on dereferencing.
I suggest you to read some articles on reference counted memory management as it might be not obvious if you are new to Objective-C, and often leads to mistake (i.e: autorelease is not magic at all)
best way to avoid all memory leaks here is just simply use #property (nonatomic, retain) NSMutableArray *sections; by using property you can be sure that all men management works will be correctly managed by system. Just don't forget that property retains value when you doing setSections:, so that you need to pass autoreleased object here.
self.sections = [NSMutableArray array];
...
[self.sections addObject:section];
Also to avoid all problem try to make all objects which should live only in this method autorelease. Like this:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSString *responseString = [[[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding] autorelease];
NSDictionary *results = [responseString JSONValue];
NSMutableArray *jSections = [results objectForKey:#"Items"];
self.sections = [NSMutableArray array];
for (NSArray* jSection in jSections) {
Section* section = [[[Section alloc] init] autorelease];
section.Id = [jSection objectForKey:#"Id"];
section.Description = [jSection objectForKey:#"Description"];
section.Image = [jSection objectForKey:#"Image"];
section.Parent = [jSection objectForKey:#"Parent"];
section.ProductCount = [jSection objectForKey:#"ProductCount"];
[self.sections addObject:section];
}
[delegate sectionsLoaded];
[self.view reloadData];
}
And also most of object you trying to release already autoreleased:
all params passed into your method shouldn't be released manually, check I think JSONValue also should returns autoreleased object and anything you getting by enumerating or by call objectForKey:
I developing an application, in which i working with database manipulation. The method i have written in database class as follows.
-(NSMutableArray *)getData: (NSString *)dbPath{
NSMutableArray *dataArray = [[NSMutableArray alloc] init];
if(sqlite3_open([dbPath UTF8String], &database) == SQLITE_OK){
NSString *sqlQuery = [NSString stringWithFormat:#"SELECT empID, addText FROM Employee WHERE nameID = %#", nameID];
sqlite3_stmt *selectstmt;
if(sqlite3_prepare_v2(database, [sqlQuery UTF8String], -1, &selectstmt, NULL) == SQLITE_OK){
while (sqlite3_step(selectstmt) == SQLITE_ROW){
[dataArray addObject:[[NSMutableDictionary alloc] init]];
[[dataArray lastObject] setObject:[NSString
stringWithFormat:#"%d", sqlite3_column_int(selectstmt, 0)] forKey:#"empID"];
[[dataArray lastObject] setObject:[NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt,1)] forKey:#"addText"];
}
}
sqlite3_finalize(selectstmt);
}
sqlite3_close(database);
return dataArray;
}
The above code work fine on the simulator but cannot on device.
I also was tracing the memory leaks , in which i founding that memory leak in above method code. But i not able to solve the that memory leak.
Now i also find out memory leak in following method.
(id)initWithString:(NSString *)str attributes:(NSDictionary *)attributes
{
if ((self = [super init]))
{
_buffer = [str mutableCopy];
_attributes = [NSMutableArray arrayWithObjects:[ZAttributeRun attributeRunWithIndex:0 attributes:attributes], nil];
}
return self;
}
The leak near _buffer = [str mutableCopy];. And leak trace gives me in the output continuous increasing NSCFString string allocation. How i maintain it?
Thanks in advance.
Your leak is that you don't release either the dataArray object, nor the mutable dictionaries you create in the while loop. Consider autoreleasing the mutable array, and manually releasing the dictionaries after you add them to the array.
As for why it "doesn't work" on the device, you need to be more specific about what happens and why that isn't what you expect.
Your inner loop leaks the NSMutableDictionary objects, as you should release them after adding to the array, i.e.
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
[dict setObject:[NSString stringWithFormat:#"%d", sqlite3_column_int(selectstmt, 0)] forKey:#"empID"];
[dict setObject:[NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt,1)] forKey:#"addText"];
[dataArray addObject:dict];
[dict release];
Also, your whole method should most probably return an autoreleased object by naming convention. Not sure if this is a leak - depends on how you call that method and if you release the returned value.
So maybe use
return [dataArray autorelease];
From the first glance you have 2 places where leaks can be:
NSMutableArray *dataArray = [[NSMutableArray alloc] init];
...
return dataArray;
Caller method is responsible for releasing array returned from your method - check if it does.
Also your method name is not consistent with obj-c guidelines - they suggest that methods returning non-autoreleased object( so caller is responsible for releasing them) should contain create, alloc, copy in their name. So it could be better to return autoreleased array (return [dataArray autorelease]; from this method and let caller decide whether it needs to retain array or not.
Second place is
[dataArray addObject:[[NSMutableDictionary alloc] init]];
It is leaking dictionary object, you should probably just write
[dataArray addObject:[NSMutableDictionary dictionary]];
Your method contains two call to +alloc that don't have corresponding calls to -release or -autorelease.
NSMutableArray *dataArray = [[NSMutableArray alloc] init];
...
[dataArray addObject:[[NSMutableDictionary alloc] init]];
You can rewrite these lines like this to get rid of the leak:
NSMutableArray *dataArray = [NSMutableArray array];
...
[dataArray addObject:[NSMutableDictionary dictionary]];
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.
I have noted several other threads on this topic and have tried wrapping my threaded code with:
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
[pool release];
but the errors still come.
I am using a static method to instantiate a dictionary of words.
Here is some code:
-(id)init
[NSThread detachNewThreadSelector:#selector(loadDictionary:) toTarget:[IntroScreen class] withObject:nil];
[NSThread setThreadPriority:1.0];
return self;
}
+(void)loadDictionary:(id)param
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
[[SimpleAudioEngine sharedEngine] preloadEffect:#"click.wav"];
[[SimpleAudioEngine sharedEngine] preloadEffect:#"pop.wav"];
[[SimpleAudioEngine sharedEngine] preloadEffect:#"dink.wav"];
[[SimpleAudioEngine sharedEngine] playBackgroundMusic:#"musicloop.wav"];
[WordDictionary configDictionary];
[pool release];
}
+(void)configDictionary
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
Serializer * mySerializer = [[Serializer alloc] init];
[WordDictionary setDictionary:[mySerializer readApplicationPlist:#"x"]];
NSString * string;
NSString *filePath = [[[NSBundle mainBundle] resourcePath]
stringByAppendingPathComponent:#"x.txt"];
NSString *info = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
NSArray *arrayOfLines = [info componentsSeparatedByString:#"\r\n"];
[WordDictionary setDictionary:[[NSMutableDictionary alloc] init]];
[NSMutableDictionary dictionaryWithContentsOfFile:filePath];
int len = [arrayOfLines count];
for(int i = 0; i < len; i++)
{
string = [arrayOfLines objectAtIndex:i];
NSString * blankString = [NSString stringWithString:#""];
[[WordDictionary dictionary] setObject:blankString forKey:string];
double calc = ((double)i / (double)len) * 100.0;
[WordDictionary setProgress:(int)calc];
}
[mySerializer writeApplicationPlist:[WordDictionary dictionary] toFile:#"s"];
[WordDictionary setProgress:100];
[pool release];
}
Is there something I should know about using static class methods with new selector threads?
Thank you for your help
First, there are no static methods in Objective-C. There are class methods.
Secondly, your code shows both methods wrapped in autorelease pools. The warning must be coming from somewhere else.
Finally, your code leaks like a sieve. You aren't following the memory management rules. And there are some nonsense statements in there.
Specifically:
[WordDictionary setDictionary:[[NSMutableDictionary alloc] init]];
Unless +setDictionary: is breaking the memory management rules, the above leaks.
This statement [NSMutableDictionary dictionaryWithContentsOfFile:filePath]; effectively does nothing unless you do something with the return value.
Also, mySerializer is leaking.
Try running the analyzer over your code and fixing the problem. You should also read this and this.
Ah the [NSMutableDictionary dictionaryWithContentsOfFile:filePath]; was part of an experiment I was attempting to make the dictionary access faster. I should have removed it from this example.
I have just read the memory management rules, and understand that
[WordDictionary setDictionary:[[NSMutableDictionary alloc] init]]; appears to be poorly planned instantiation because I have no way to release it from within configDictionary as the reference is lost. But actually I don't ever want to release it, it lives for the entire lifetime of my application. Probably bad practice just the same.
mySerializer should definitely be released at the bottom.
I was just wondering if class methods had any special rules regarding autorelease pools and memory.
I will look over the documents you sent me and try to figure out the Analyzer, thank you for your help.
According to Instruments analysis, I have a memory leak here - and - I'm unsure as to how to properly release the scoresArray object that I have created.
This code does work properly, apart from the leakage. I release the highScoresArray object later in the code - but attempts to release the scoresArray kill the app. I thought that when I released highScoresArray, that I would release scoresArray, since they both point to the same location in memory. If anyone can point out where my thinking is flawed, that would be great.
- (void) readScoresFile {
// Read the Scores File, if it exists
NSString *filePath = [self scoresFilePath];
// Only load the file if it exists at the path
if ([[NSFileManager defaultManager] fileExistsAtPath: filePath]) {
scoresFileExistsFlag = YES;
NSLog(#"SCORES FILE EXISTS - THEREFORE LOAD IT");
NSMutableArray *scoresArray = [[NSMutableArray alloc] initWithContentsOfFile: filePath];
highScoresArray = scoresArray;
} else {
scoresFileExistsFlag = NO;
NSMutableArray *scoresArray = [[NSMutableArray alloc] init];
highScoresArray = scoresArray;
// No Scores File exists - we need to create and save an empty one.
int counter = 1;
while (counter <= 5) {
// Set up a date object and format same for inclusion in the Scores file
NSDate *now = [[NSDate alloc] init];
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"yyyy.MM.dd"];
NSString *theDateNow = [dateFormat stringFromDate:now];
// Add the score data (Score and User and date) to the runScoreDataDictionary
runScoreDataDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:0], #"score",
[NSNumber numberWithInt:0], #"landings",
currentUser, #"user",
theDateNow, #"date", nil];
//NSLog(#"Dictionary contains: %#", runScoreDataDictionary);
// Add the dictionary to the highScoreArray
[highScoresArray addObject:runScoreDataDictionary];
//NSLog(#"OBJECTS in ARRAY: %i", [highScoresArray count]);
[self writeScoresFile]; // Write the empty scores file to disk
[now release];
[dateFormat release];
++counter;
//[scoresArray release]; // TESTING TO SEE IF THIS KILLS - YES KILLS
}
}
}
I'm guessing highScoresArray is an instance variable (since it's not declared anywhere in the method you listed). This means that upon creation of scoresArray (which is the same object as highScoresArray) it has a retain count of 1. You don't retain it, so releaseing it will decrement its retain count to 0 and it will be cleaned up -- not a good thing for an instance variable.
I'm also not sure why you do this:
NSMutableArray *scoresArray = [[NSMutableArray alloc] init];
highScoresArray = scoresArray;
You don't seem to need to use scoresArray anywhere else, so you could just do this:
[highScoresArray release]; // Release the old object
highScoresArray = [[NSMutableArray alloc] init];
I release the highScoresArray object later in the code - but attempts to release the scoresArray kill the app. I thought that when I released highScoresArray, that I would release scoresArray, since they both point to the same location in memory
As long as you haven't changed the highScoresArray pointer to point to another object, releasing it will be the same as releasing scoresArray.
NSMutableArray* highScoresArray;
NSMutableArray* scoresArray = [[NSMutableArray alloc] init];
highScoresArray = scoresArray;
[highScoresArray release]; // same as `[scoresArray release];`
But if you change either of them afterwards to point to another object, releasing them won't be equivalent:
NSMutableArray* highScoresArray;
NSMutableArray* scoresArray = [[NSMutableArray alloc] init];
highScoresArray = scoresArray;
// ... Now make `highScoresArray` point to another object ...
highScoresArray = [[NSMutableArray alloc] init];
// Now you should release both as they point to different objects.
[highScoresArray release];
[scoresArray release];
Of course, simply calling addObject doesn't alter the pointer. It changes the object being pointed to. Only reassigning the pointer to another object matters here.