I am having a specific leak that I can't seem to dig to the bottom of, cause my search always ends up in some Apple libraries. Any help from some veterans at dealing with this would be appreciated.
Here is the relevant source code: (leak indicated with a comment)
- (void)viewDidLoad {
//[super viewDidLoad];
NSManagedObjectContext *context = [(iEatAppDelegate*)[[UIApplication sharedApplication] delegate] managedObjectContext];
addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:creator action:#selector(launchCreatorWindow)];
self.navigationItem.rightBarButtonItem = addButton;
NSFetchRequest *fetReq = [[NSFetchRequest alloc] init];
[fetReq setEntity:[NSEntityDescription entityForName:entityName inManagedObjectContext:context]];
NSError *e = nil;
NSArray *temp = [context executeFetchRequest:fetReq error:&e];
//leaking here according to performance tool
self.tableObjects = [[NSMutableArray alloc] initWithArray:temp];
if (e) {
NSLog(#"%#, %#", e, [e description]);
}
[fetReq release];
}
I tried releasing temp, but it crashed with the EXC_BAD_ACCESS which i believe means it is autoreleased already.
The leaks performance tool says it is a Category: CFArray (store-deque) Event: Malloc
also says my library is responsible. This is the stack trace, number 8 being my viewDidLoad frame:
0 CoreFoundation __CFAllocatorSystemAllocate
1 CoreFoundation CFAllocatorAllocate
2 CoreFoundation _CFAllocatorAllocateGC
3 CoreFoundation _CFArrayReplaceValues
4 CoreFoundation CFArrayReplaceValues
5 CoreFoundation -[__NSPlaceholderArray initWithObjects:count:]
6 CoreFoundation -[NSArray initWithArray:copyItems:]
7 CoreFoundation -[NSArray initWithArray:]
This one really has me stuck, any help is greatly appreciated.
This causes your leak:
self.tableObjects = [[NSMutableArray alloc] initWithArray:temp];
You create an array with a retain count of 1, then you use self.tableObjects which (if that property is marked as retain) brings the count up to 2.
Then in dealloc when you release the array the count is back down to 1, not 0 - so the array is never released.
Instead, just do this:
self.tableObjects = [NSMutableArray arrayWithArray:temp];
That returns an autoreleased array, so the eventual retain count will be only 1.
The answer is I am missing something basic.
[NSMutableArray alloc]
retain count = 1
self.tableObjects =
retain count = 2
dealloc
retain count still not 0
leak.
Sorry about this
This isn't the cause of your leak but I thought it'd be worth pointing out anyway: You should release addbutton right after you have assigned it to the rightBarButtonItem seeing as it is not going to be needed/used again:
self.navigationItem.rightBarButtonItem = addButton;
[addButton release];
Related
I'm occasionally getting this crash, which is very hard to reproduce:
0 CoreFoundation 0x39ff73e2 __exceptionPreprocess + 158
1 libobjc.A.dylib 0x3905095e objc_exception_throw + 26
2 CoreFoundation 0x39ffaf2c -[NSObject(NSObject) doesNotRecognizeSelector:] + 180
3 CoreFoundation 0x39ff9648 ___forwarding___ + 388
4 CoreFoundation 0x39f51204 _CF_forwarding_prep_0 + 20
5 Foundation 0x32914bec -[NSDictionary(NSKeyValueCoding) valueForKey:] + 28
6 MyAppName 0x00032112 -[myViewController stopPlayersWithVolume:] (myViewController.m:151)
From this code:
- (void)stopPlayersWithVolume:(double)volume
{
for (NSString *file in players)
{
AVAudioPlayer *player = [players valueForKey:file];
if (player.volume == volume || volume == 0)
[player stop];
}
}
players is an NSMutableDictionary property, accessed without self. because I don't believe it's needed with ARC. The keys are filenames (e.g. "mysound.mp3") and the values are AVAudioPlayer objects.
Is the stack trace saying that the parameter file I'm passing to [NSMutableDictionary valueForKey] is not actually an NSString, and hence the unrecognised selector? It is always an NSString in the debugger, as you'd expect as it comes from a fast enumeration through the dictionary, but the crash never occurs in the debugger.
My only thought is that there's a threading issue corrupting the dictionary. The code above is being fired by an NSTimer but that's just a message via the run loop, not a separate thread, so I believe there should be no need to worry about cross-thread access?
Any suggestions appreciated. Sorry to just dump this out but I'm stuck.
Edit:
The players dictionary is allocated in viewDidLoad thus:
players = [[NSMutableDictionary alloc] init];
And declared as follows:
#property (retain, nonatomic) NSMutableDictionary *players;
And synthesised as follows:
#synthesize players;
It's likely that your players dictionary or its contents are being deallocated while you're iterating through them. Can you turn on NSZombies and try to reproduce the crash in the debugger? It will give you more information about what object is being deallocated.
The code to allocate the dictionary (and set up the audio stuff) looked like this:
NSError *error = [[NSError alloc] init];
NSArray *names = [fm contentsOfDirectoryAtPath:resourcePath error:&error];
players = [[NSMutableDictionary alloc] init];
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient error:&error];
[[AVAudioSession sharedInstance] setActive:YES error:&error];
I must have copied and pasted it from somewhere, because that use of NSError is wrong, as far as I know, though hardly likely to cause a problem. I changed the code to this:
NSArray *names = [fm contentsOfDirectoryAtPath:resourcePath error:nil];
players = [[NSMutableDictionary alloc] init];
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient error:nil];
[[AVAudioSession sharedInstance] setActive:YES error:nil];
And the problem goes away, it appears. Can't be 100% sure but I can reproduce the crash within 10 attempts with the former code and never with the latter. No idea why.
I've never encountered such issue before.. And now i don't know how to handle this, and hoping for some help.
I am pushing a view controller(lets say exampleViewController) to the navigation controller. In my exampleViewController on viewdidload i add an imageview with an image. On dealloc i remove this imageview from superview, then release any retained views. The instruments leak tool doesn't show any leaks, but when i open instruments- "activity monitor" and then i see that when i push my exampleViewController real memory increases in 5 MB, and when i pop my exampleViewController, memory only decreases in 3 MB.. So if i push and pop this exampleViewController a lot of times, i get a memory warning and after that app quits.
I'm definitely doing something wrong, because other view controllers behave as expected. So the problem is i don't know what am i doing wrong, and hope for you guys to suggest some ways of how i could track down what is causing this.
I have tried some tools in instruments like alloc tool and then marking heap, and some similar things, but that doesn't show what gets leaked :/
Thanks in advance!!
EDIT:
Now when marking heaps in instruments it seems executeFetchRequest: is leaking, Am i doing something wrong?
+ (Question *)getQuestionWithId:(NSString *)questionId
{
Question *resultQuestion = nil;
AppDelegate *appDelegate = (AppDelegate*)[UIApplication sharedApplication].delegate;
//geting context from appdelegate
NSManagedObjectContext *context = appDelegate.managedObjectContext;
//form fetch request with predicate
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Question"
inManagedObjectContext:context];
NSPredicate *query = [NSPredicate predicateWithFormat:#"questionId=%#",questionId];
NSPredicate *query1 = [NSPredicate predicateWithFormat:#"type='ke'"];
NSPredicate *predicates = [NSCompoundPredicate andPredicateWithSubpredicates:[NSArray arrayWithObjects:query,query1, nil]];
[fetchRequest setPredicate:predicates];
[fetchRequest setEntity:entity];
NSError *error = nil;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
[fetchRequest release];
resultQuestion = [fetchedObjects lastObject];
return resultQuestion;
}
Might be, that if your image is loaded with imageNamed, the cache doesn't get emptied on dealloc. Have you tried loading the image with
UIImage *image = [UIImage imageWithContentsOfFile:path];
instead of using
myImageView.image = [UIImage imageNamed:#"someImage.png"]
Of course this might only help "if" you use imageNamed.
...On dealloc i remove this imageview from superview...
Never do this in -(void)dealloc, as this method is supposed for memory management.
Remove the imageview in -(void)viewWillDissappear instead (if you really need this). As about your problem, read this, I believe your issue is somewhere beyond the imageView you are talking about.
I have tried to use the Analyze instrument to see the memory leaks, and Xcode gives me a memory leak in this point:
.h
#property (strong, nonatomic) NSManagedObjectContext *managedObjectContext;
.m
#synthesize managedObjectContext = __managedObjectContext;
then in the code I do this:
AppDelegate *appController = [[UIApplication sharedApplication] delegate];
self.managedObjectContext = [[NSManagedObjectContext alloc] init];
[self.managedObjectContext setUndoManager:nil];
[self.managedObjectContext setPersistentStoreCoordinator: [appController persistentStoreCoordinator]];
and in the dealloc this:
- (void)dealloc
{
[__managedObjectContext release];
[super dealloc];
}
It gives me a memory leak on this line:
[self.managedObjectContext setUndoManager:nil];
for this object:
self.managedObjectContext = [[NSManagedObjectContext alloc] init];
I have released it in the dealloc, why a memory leak there?
Your _managedObjectContext leaks because its retain count is increased twice, but you only release it once. You have made your property strong which means it will retain the object when it is assigned to it. But you also allocate managedObjectContext without autoreleasing (or manually releasing) it. Hence the error is in this line:
self.managedObjectContext = [[NSManagedObjectContext alloc] init];
The object you allocate will have a retain count of 1, but the synthesized property will retain it one more time, hence giving it a retain count of 2. When you release it in dealloc, it will stay around since it still has a retain count of 1.
What you need to do is either autorelease the object you allocate:
self.managedObjectContext = [[[NSManagedObjectContext alloc] init] autorelease];
or not call the synthesized setter, but assign directly to _managedObjectContext:
_managedObjectContext = [[NSManagedObjectContext alloc] init];
I would recommend the former since the latter will leak if _managedObjectContext is not nil. However if you're sure _managedObjectContext is nil before the assignment, you can pick either one you want.
This line...
self.managedObjectContext = [[NSManagedObjectContext alloc] init];
...increases the retain count by two, one for the alloc and one for the "strong" on the property.
Try:
__managedObjectContext = [[NSManagedObjectContext alloc] init];
I'm trying to resolve a memory leak but I can't find any solution.
Instruments says that there is a leak in this method:
- (void)refreshData {
Sn0werSp33dAppDelegate *appDelegate = [[Sn0werSp33dAppDelegate alloc] init];
NSFetchRequest *coreDataNewsFetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"News" inManagedObjectContext:managedObjectContext];
[coreDataNewsFetchRequest setEntity:entity];
self.managedObjectContext = appDelegate.managedObjectContext;
self.newsArray = [[managedObjectContext executeFetchRequest:coreDataNewsFetchRequest error:nil] mutableCopy];//Intruments says that here is the memory leak :(
[appDelegate release];
[coreDataNewsFetchRequest release];
[entity release];
}
newsArray is declared in my .h as an NSMutableArray and it has a property:
#property (nonatomic, retain) NSMutableArray *newsArray;
I've tried many things but at all times, that things weren't working.
I'm running XCode 3.2.5 with iPhone SDK 4.2.1 and Instruments 2.7.
When you do
self.newsArray = something;
that something is retained, because you added retain to the newsArray property.
But mutableCopy also returns an object with a retain count increased by 1. So after the method finishes, your newsArray has a retain count one higher than what you really want, which is the memory leak that was detected.
Solution: Replace the line where you assign self.newsArray with
self.newsArray = [[[managedObjectContext executeFetchRequest:coreDataNewsFetchRequest error:nil] mutableCopy] autorelease];
mutableCopy makes a copy and retains it, so you need to release the copy you've created. Try changing this:
self.newsArray = [[managedObjectContext executeFetchRequest:coreDataNewsFetchRequest
error:nil] mutableCopy];
To this:
self.newsArray = [[[managedObjectContext executeFetchRequest:coreDataNewsFetchRequest
error:nil] mutableCopy] autorelease];
As an aside, creating a new object of your app delegate class is a little unusual and might not give you the result you expect. Conventionally you instantiate one app delegate (by default this is done for you in MainWindow.xib) and then refer to it throughout your app using:
FooAppDelegate *appDelegate = (FooAppDelegate*)[[UIApplication sharedApplication] delegate];
My controller get data from function in delegat:
- (NSArray *)getChapters {
NSMutableArray *list = [[NSMutableArray alloc] init]; //memory leak
if (chapter_statement == nil) {
const char *sql = "SELECT DISTINCT 'Глава '||chapter FROM verses WHERE book=? ORDER by chapter";
if (sqlite3_prepare_v2(database, sql, -1, &chapter_statement, NULL) != SQLITE_OK) {
NSAssert1(0, #"Error: failed to prepare statement with message '%s'.", sqlite3_errmsg(database));
}
}
sqlite3_bind_int(chapter_statement, 1, self.book);
while (sqlite3_step(chapter_statement) == SQLITE_ROW) {
NSString *body = [NSString stringWithUTF8String:(char *)sqlite3_column_text(chapter_statement, 0)];
[list addObject:body];
[body release];
}
sqlite3_reset(chapter_statement);
return list;
}
and use it in controller:
- (void)viewWillAppear:(BOOL)animated {
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
self.listChapters = [[NSArray alloc] initWithArray:[appDelegate getChapters]];
[self.listChapters release];
}
Leaks shows a memory leak at : NSMutableArray *list = [[NSMutableArray alloc] init];
If I do return like return [list autorelease]; app crashes in viewWillAppear.
How to fix this problem?
Returning [list autorelease] is the right thing to do. Your problem is the [body release] which you don't need. The -[NSString stringWithUTF8String:] returns an autoreleased NSString. The explicit [body release] means the list has pointers to deallocated objects.
Delete the [body release] line and put back the return [list autorelease] and it should work.
You can also run the static analyzer (Cmd-shift-A) to ask the compiler to find other memory management issues like this one.
You should autorelease in getChapters and you should not release self.listChapters in viewWillAppear. It's just about never a good idea to write [self.something release], because then you're potentially deallocating an object that you still have assigned to that property.
I strongly recommend you read the memory management rules. They're not lengthy or difficult, and once you read through and understand them, you will never even have to think about something like this again.
Lets count!
1.) You allocate the leaking array here:
NSMutableArray *list = [[NSMutableArray alloc] init];
retainCount = 1.
2.) You add the array to another array here:
self.listChapters = [[NSArray alloc] initWithArray:[appDelegate getChapters]];
What the new array (listChapters) does is retaining your leaking array.
retainCount = 2.
3.) You release the array (listChapters) that contains the leaking array:
[self.listChapters release];
What listChapters does here is also releasing all containing objects once, including your leaking array. Also, all references to your leaking array are lost after this line.
retainCount = 1