Memory-management in iOS Programming - iphone

I have a little problem with memory management in my iOS App. I load an XML and set all Values in this XML to Spezific Objects. Now my problem is when i reload the XML every 15 - 20 reloads of this XML my app Crash on Parsing here is a sample of my parser.
EDIT: Here ist the ERROR when NSZombie is Enabled if NSZombie is disabled I didn't get an ERROR message.
-[CFNumber retain]: message sent to deallocated instance
thanks for help.
EDIT:
the beginning of my Code:
- (id)init
{
self = [super init];
if (self) {
TheObjects *theObjects = [[TheObjects alloc] init];
[self parse];
}
return self;
}
- (void) reload{
reload = YES;
TheObjects *theTmpObjects = [[TheObjects alloc] init];
[self parse];
}
- (void)parse{
for (id xmlOBject in xmlObjects){
MyObject *object = [[MyObject alloc] init];
object.number1 = [NSNumber numberWithInt:1];
object.number2 = [NSNumber numberWithInt:2];
object.number3 = [NSNumber numberWithInt:3];
if (reload)
[theTmpObjects.objects addObject:object];
else
[theObjects.objects addObject:object];
[object release];
}
//later in my code
TheObjects *localTheTmpObjects = nil;
if (reload){
localTheTmpObjects = theObjects;
theObjects = theTmpObjects;
}
if ([delegate respondsToSelector:#selector(finished:)]){
[delegate performSelector:#selector(finished:) withObject:theObjects];
}
if(reload)
[localTheTmpObjects release];
}

remove the line [localTheTmpObjects release]
you don't own the object
at the end, call the `[localTheTmpObjects autorelease];`
this is because if you release array, all its objects are released and hence may cause crash, when your array may in use
- (id)init
{
self = [super init];
if (self) {
TheObjects *obbjects = [[TheObjects alloc] init];
theObjects = objects;
[objects releas];
[self parse];
}
return self;
}
- (void) reload{
reload = YES;
TheObjects *obbjects = [[TheObjects alloc] init];
thetmpObjects = objects;
[objects releas]; [self parse];
}
- (void)parse{
for (id xmlOBject in xmlObjects){
MyObject *object = [[MyObject alloc] init];
object.number1 = [NSNumber numberWithInt:1];
object.number2 = [NSNumber numberWithInt:2];
object.number3 = [NSNumber numberWithInt:3];
if (reload)
[theTmpObjects.objects addObject:object];
else
[theObjects.objects addObject:object];
[object release];
}
//later in my code
TheObjects *localTheTmpObjects = nil;
if (reload){
localTheTmpObjects = theObjects;
theObjects = theTmpObjects;
}
if ([delegate respondsToSelector:#selector(finished:)]){
[delegate performSelector:#selector(finished:) withObject:theObjects];
}
}

Related

Memory management advice - how to handle Zombies?

In the below sample code, I am a bit lost as to why I am getting a NZombie on the line:
[Category getInitialDataToDisplay:[self getDBPath]];
I have looked through SO posts and other documentation but am new to objective-c and am spinning my wheels on this.
- (void)applicationDidFinishLaunching:(UIApplication *)application {
//Copy database to the user's phone if needed.
[self copyDatabaseIfNeeded];
// Init the Array
activeCategories = [[NSMutableArray alloc] init];
activeSubjects = [[NSMutableArray alloc] init];
categories = [[NSMutableArray alloc] init];
subjects = [[NSMutableArray alloc] init];
quotes = [[NSMutableArray alloc] init];
quoteMaps = [[NSMutableArray alloc] init];
//Initialize the Category array.
NSMutableArray *tempArray = [[NSMutableArray alloc] init];
self.categories = tempArray;
[tempArray release];
//Once the db is copied, get the initial data to display on the screen.
[Category getInitialDataToDisplay:[self getDBPath]];
//populate active subjects and categories:
activeCategories = [self getActiveCategories];
activeSubjects = [self getActiveSubjects];
// sort data
NSSortDescriptor *categorySorter;
NSSortDescriptor *subjectSorter;
categorySorter = [[NSSortDescriptor alloc]initWithKey:#"category_title" ascending:YES];
subjectSorter = [[NSSortDescriptor alloc]initWithKey:#"title" ascending:YES];
NSArray *sortDescriptorsCat = [NSArray arrayWithObject:categorySorter];
NSArray *sortDescriptorsSub = [NSArray arrayWithObject:subjectSorter];
[self.categories sortUsingDescriptors:sortDescriptorsCat];
[self.subjects sortUsingDescriptors:sortDescriptorsSub];
[categorySorter release];
[subjectSorter release];
// Configure and show the window
[window addSubview:[navigationController view]];
[window makeKeyAndVisible];
}
...
- (void)dealloc {
[activeSubjects release];
[activeCategories release];
[categories autorelease];
[subjects autorelease];
[quotes autorelease];
[quoteMaps autorelease];
[navigationController release];
[window release];
[super dealloc];
}
Here is the getInitialDataToDisplay:
+ (void) getInitialDataToDisplay:(NSString *)dbPath {
// Use this section to bring in database and populate the array
FMDatabase *database = [FMDatabase databaseWithPath:dbPath];
[database open];
QuotesAppDelegate *appDelegate = (QuotesAppDelegate *)[[UIApplication sharedApplication] delegate];
//appDelegate.categories = [appDelegate.categories sortedArrayUsingSelector:#selector(caseInsensitiveCompare:)];
//POPULATE THE SUBJECT
FMResultSet *result_subjects = [database executeQuery:#"select * from SUBJECT"];
while([result_subjects next]) {
NSInteger primaryKey = [result_subjects intForColumn:#"SUBJECT_ID"];
Subject *sub = [[Subject alloc] initWithPrimaryKey:primaryKey];
sub.title = [result_subjects stringForColumn:#"SUBJECT"];
sub.category_title = [result_subjects stringForColumn:#"CATEGORY"];
sub.active = [result_subjects intForColumn:#"ACTIVE"];
sub.isDirty = NO;
[appDelegate.subjects addObject:sub];
[sub release];
}
FMResultSet *result_categories = [database executeQuery:#"select distinct category from SUBJECT"];
while([result_categories next]) {
Category *cat = [[Category alloc] init];
cat.category_title = [result_categories stringForColumn:#"CATEGORY"];
NSLog(#"loading category: %#", cat.category_title);
QuotesAppDelegate *appDelegate = (QuotesAppDelegate *)[[UIApplication sharedApplication] delegate];
for (Subject *sb in appDelegate.subjects){
if([cat.category_title isEqualToString:sb.category_title]){
[cat.subjects addObject:sb];
NSLog(#" Adding subject: %# cat.subjects.count=%i", sb.title, cat.subjects.count);
}
}
[appDelegate.categories addObject:cat];
[cat release];
}
//POPULATE THE QUOTES
FMResultSet *result_quotes = [database executeQuery:#"select * from QUOTE"];
while([result_quotes next]) {
Quote *sub = [Quote alloc];
sub.quote_id = [result_quotes stringForColumn:#"QUOTE_ID"];
sub.quote_date = [result_quotes stringForColumn:#"DATE"];
sub.title = [result_quotes stringForColumn:#"DESC1"];
sub.desc2 = [result_quotes stringForColumn:#"DESC2"];
sub.excerpt = [result_quotes stringForColumn:#"EXCERPT"];
sub.note = [result_quotes stringForColumn:#"NOTES"];
sub.isDirty = NO;
[appDelegate.quotes addObject:sub];
[sub release];
}
//POPULATE THE QUOTE_MAPS
FMResultSet *result_quote_map = [database executeQuery:#"select * from QUOTE_MAP"];
while([result_quote_map next]) {
QuoteMap *sub = [QuoteMap alloc];
sub.quote_id = [result_quote_map stringForColumn:#"QUOTE_ID"];
sub.quote_map_id = [result_quote_map stringForColumn:#"QUOTE_MAP_ID"];
sub.subject_id = [result_quote_map stringForColumn:#"SUBJECT_ID"];
sub.isDirty = NO;
[appDelegate.quoteMaps addObject:sub];
[sub release];
}
[database close];
NSLog(#"Count of categories: %i", appDelegate.categories.count);
NSLog(#"Count of subjects: %i", appDelegate.subjects.count);
NSLog(#"Count of quotes: %i", appDelegate.quotes.count);
NSLog(#"Count of quoteMaps: %i", appDelegate.quoteMaps.count);
}
Here is the getDbPath:
- (NSString *) getDBPath {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES);
NSString *documentsDir = [paths objectAtIndex:0];
return [documentsDir stringByAppendingPathComponent:#"reference.db"];
}
Sometimes, the best thing to do is build->analyze ( cmd shift b ). This will point out your bug right away in almost all cases.
Best of luck!
categories = [[NSMutableArray alloc] init];
.
.
//Initialize the Category array.
NSMutableArray *tempArray = [[NSMutableArray alloc] init];
self.categories = tempArray;
[tempArray release];
u've setup categories, then setup the tempArray, replaced the one in categories with it thus making a leak, then released the temp arrayObject, which what categories is now also pointing on, so unless "self.categories" is a retained property it will be a zombie. there seems to be something wrong there.
I may need to see some more of your code (the property declarations and their synthesis to make sure.
is the Zombie called on "getInitialDataToDisplay" or on "getDBPath"
try dividing it on 2 lines to know pin point more
I think you have not declare Category as retained property in .h file.If not, add following line in your .h file
#property (nonatomic, retain) NSArray Category;
And synthesize the property in .m as-
#synthesize Category;
I think it will help ....

Incorrect decrement of the reference count of an object that is not owned at this point by the caller

Incorrect decrement of the reference count of an object that is not owned at this point by the caller on iPhone. It is happening with NSString which I clearly init and release within the for loop. I have tried to do the same as an autoreleases string but I get leaks. I assume the culprit is the stringbytrimming call. Any suggestions, by the way this does not leak, but I get the warning in build and analyze. Everything also works fine and the app does not crash.
for(int i=0;i<storyQuantity;i++) {
NSString *imageString = [[NSString alloc] init];
imageString = [[[storiesArray objectAtIndex:i] objectForKey: #"image"] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; // must add trimming to remove characters
imageLoader *imageOperation = [[imageLoader alloc] initWithImageURL:imageString target:self action:#selector(didImageLoad:) number:i];
AppDelegate_iPad *appDelegate = [[UIApplication sharedApplication] delegate];
[appDelegate.queue_ addOperation:imageOperation];
[imageOperation release];
[imageString release];
}
UPDATE - added my imageLoader class, which to the best of my knowledge does not have a leak
- (id)initWithImageURL:(NSString *)url target:(id)target action:(SEL)action number:(int)number {
if(self = [super init]) {
_action = action;
_target = target;
_number = number;
if(url == nil) {
return nil;
} else {
_imgURL = [[NSURL alloc] initWithString:[url copy]];
}
}
return self;
}
- (id)main {
NSAutoreleasePool *pool = [NSAutoreleasePool new];
if ([self isCancelled]) {
NSLog(#"OPERATION CANCELLED");
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
[pool drain];
return nil;
} else {
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
NSData *imgData = [[NSData alloc] initWithContentsOfURL:_imgURL];
UIImage *image = [[UIImage alloc] initWithData:imgData];
[imgData release];
if ([self isCancelled]) {
NSLog(#"OPERATION CANCELLED");
[image release];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
[pool drain];
return nil;
} else {
NSNumber *tempNumber = [NSNumber numberWithInt:_number];
NSDictionary *tempDict = [NSDictionary dictionaryWithObjectsAndKeys:tempNumber, #"number", image, #"image", nil];
[image release];
if([_target respondsToSelector:_action])
[_target performSelectorOnMainThread:_action withObject:tempDict waitUntilDone:NO];
}
}
[pool drain];
return nil;
}
- (void)dealloc {
[_imgURL release];
[super dealloc];
}
Since you are reassigning the imageString variable, the reference to the original object is lost. Why allocate an empty string anyway? Just change the code to
NSString *imageString = [[[storiesArray objectAtIndex:i] objectForKey: #"image"]
stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
and remove the [imageString release] and you're good to go.
Don't track reference counts as a way into understanding memory management. It's only going to confuse you. Things manipulate your objects' reference counts from deep in the framework, and if you watch those numbers jump around for (apparently) no reason, you'll just go insane and post a series of increasingly crazy questions here, which we'll then have to deal with. Believe me--we've seen it before.
So just ignore the reference count number, and make sure you're retaining and releasing objects properly.

iPhone - Objective-C NSURL Memory Leaks galore

I am getting the following memory leak when using NSURL. I use this method in quite a few different places and receive memory leaks all the time using the Leaks instruments.
Object Management:
self.objManager = [[HJObjManager alloc] init];
NSString *cacheDirectory = [NSHomeDirectory() stringByAppendingPathComponent:#"/Library/Caches/App"];
HJMOFileCache *fileCache = [[[HJMOFileCache alloc] initWithRootPath:cacheDirectory] autorelease];
self.objManager.fileCache = fileCache;
fileCache.fileCountLimit = 100;
fileCache.fileAgeLimit = 60*60*24;
[fileCache trimCacheUsingBackgroundThread];
Where it's used:
HJManagedImageV *mi = [[[HJManagedImageV alloc] initWithFrame:CGRectMake(self.myHeaderView.profilePictureImageView.bounds.origin.x,
self.myHeaderView.profilePictureImageView.bounds.origin.y,
self.myHeaderView.profilePictureImageView.bounds.size.width,
self.myHeaderView.profilePictureImageView.bounds.size.height)] autorelease];
mi.layer.masksToBounds = YES;
mi.layer.cornerRadius = 8.0;
mi.layer.borderColor = [[UIColor blackColor] CGColor];
mi.layer.borderWidth = 1.0;
mi.url = [NSURL URLWithString:profilePictureUrl];
[mi showLoadingWheel];
[self.myHeaderView.profilePictureImageView addSubview:mi];
[self.objManager manage:mi];
Dealloc:
- (void)viewDidUnload {
self.tv = nil;
self.friends = nil;
self.sortedFriends = nil;
self.detailView = nil;
self.profileView = nil;
self.objManager = nil;
[super viewDidUnload];
}
- (void)dealloc {
[tv release];
[friends release];
[sortedFriends release];
[detailView release];
[profileView release];
[objManager release];
[super dealloc];
}
You can use stack trace to determine the place of leak.
Edit:
make sure you are releasing the mi.url in dealloc method:
-(void) dealloc {
//some other releases
self.url = nil;
[super dealloc];
}

Problem with memory manament in Objective-C

I have a method:
-(NSArray *)doSomething{
NSArray *array = [[NSArray alloc] initWithObjects:#"Huy 1",#"Huy 2",#"Huy 3",nil];
[array release];
return array;
}
and
- (void)viewDidLoad {
[super viewDidLoad];
NSArray *array = [self doSomething];
if(array&&array.count>0){
NSLog([NSString stringWithFormat:#"%#\n",[array objectAtIndex:1]]);
}
else{
NSLog(#"Null");
}
}
I thinks i released array on doSomething() so it won't return NSArray which i created on doSomething(). I don't know it still print "Huy 2"? Anybody can tell me why?
-(NSArray *)doSomething
{
NSArray *array = [[NSArray alloc] initWithObjects:#"Huy 1",#"Huy 2",#"Huy 3",nil];
return [array autorelease];
}
Memory Management Programming Guide
- (void)viewDidLoad
{
[super viewDidLoad];
NSArray *array = [self doSomething];
if([array count]) //if array is nil this will evaluate to false. If count is 0 this will evaluate to false too.
{
NSLog(#"%#\n", [array objectAtIndex:1]); //NSLog insert values for you
}
else
{
NSLog(#"Null");
}
}
If this works with release instead of autorelease it's probably because the memory that the array was using has yet to be used by anything else. This is just chance. I suspect that if you allocate an object after NSArray *array = [self doSomething]; you will get unexpected results.
Use the method in following way:
-(NSArray *)doSomething{
NSArray *array = [[NSArray alloc] initWithObjects:#"Huy 1",#"Huy 2",#"Huy 3",nil];
return [array autorelease];
}

Memory leaks in NSMutableDictionary

My coding contains a memory leak, and somehow I can't find the leak.
Leaks points me in the direction of the way I create "ReportDetailItems"
e.g. areaContainer = [[[ReportDetailItem alloc] init] autorelease];
I've been looking at this for hours and I am at a total loss, the objects reported leaking are "ReportDetailItem", and the NSMutableDictionary contained in those objects.
Please advice.
------[ReportDetailItem.h
#interface ReportDetailItem : NSObject
{
NSNumber *total;
NSMutableDictionary *items;
}
#property (nonatomic, retain) NSNumber *total;
#property (nonatomic, retain) NSMutableDictionary *items;
- (NSString *)description;
#end
------[ReportDetailItem.m
#synthesize items, total;
- (id)init {
if (self = [super init]) {
self.items = [NSMutableDictionary dictionaryWithCapacity:0];
DLog("Alloc: %d", [items retainCount]);
}
return self;
}
- (NSString *)description {
return #"ReportDetailItem";
}
- (void)release {
[super release];
}
- (void)dealloc {
[self.items release];
[self.total release];
items = nil;
total = nil;
[super dealloc];
}
#end
------[Leaking code
NSError *error;
NSArray *data = [self.managedObjectContext executeFetchRequest:request error:&error];
if (data == nil || [data count] == 0) {
DLog(#"No data.")
} else {
for (int i=0; i < [data count]; i++) {
TaskEntity *task = [data objectAtIndex:i];
NSString *areaKey = task.activity.project.area.title.text;
NSString *projectKey = task.activity.project.title.text;
NSString *activityKey = task.activity.title.text;
ReportDetailItem *areaContainer;
if (![dataSource objectForKey:areaKey]) {
areaContainer = [[[ReportDetailItem alloc] init] autorelease];
} else {
areaContainer = [dataSource objectForKey:areaKey];
}
areaContainer.total = [NSNumber numberWithInt:([task.seconds intValue] + [areaContainer.total intValue])];
[dataSource setObject:areaContainer forKey:areaKey];
ReportDetailItem *projectContainer;
if (![areaContainer.items objectForKey:projectKey]) {
projectContainer = [[[ReportDetailItem alloc] init] autorelease];
} else {
projectContainer = [areaContainer.items objectForKey:projectKey];
}
projectContainer.total = [NSNumber numberWithInt:([task.seconds intValue] + [projectContainer.total intValue])];
[areaContainer.items setObject:projectContainer forKey:projectKey];
ReportDetailItem *activityContainer;
if (![projectContainer.items objectForKey:activityKey]) {
activityContainer = [[[ReportDetailItem alloc] init] autorelease];
} else {
activityContainer = [projectContainer.items objectForKey:activityKey];
}
activityContainer.total = [NSNumber numberWithInt:([task.seconds intValue] + [activityContainer.total intValue])];
[projectContainer.items setObject:activityContainer forKey:activityKey];
}
}
I found it, the leak was located in the way I allocated the "dataSource"
---[Leak
- (void)viewDidLoad {
[super viewDidLoad];
self.dataSource = [[NSMutableDictionary alloc] init];
[self fetchData];
}
---[No leak
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
self.dataSource = dict;
[dict release];
[self fetchData];
}
I'm pretty skeptic about the two ways u assign pointers to the ReportDetailItem. Why are you trying to autorelease the object in the first place? If not try this
ReportDetailItem *projectContainer;
if (![areaContainer.items objectForKey:projectKey]) {
projectContainer = [[ReportDetailItem alloc] init];
} else {
projectContainer = [[areaContainer.items objectForKey:projectKey] retain];
}
projectContainer.total = [NSNumber numberWithInt:([task.seconds intValue] + [projectContainer.total intValue])];
[areaContainer.items setObject:projectContainer forKey:projectKey];
if(projectContainer) {
[projectContainer release];
projectContainer = nil;
}