Memory leak problem and i need help #2 [duplicate] - iphone

I am very new at this and seems to have a leak in this piece of code that i cannot fix:
The Instruments shows on this line with a 100%:
NSMutableArray *read_Question = [[NSMutableArray alloc] initWithCapacity: 0];
I have tried all kind of things but not been able to fix it.
Anyone nice that can advice me how to proceed?
- (NSMutableArray *)readQuestion: (int)questionNr {
NSMutableArray *read_Question = [[NSMutableArray alloc] initWithCapacity: 0];
NSError *error;
//=========PREPARE CORE DATA DB===========//
if (managedObjectContext == nil) {
managedObjectContext = [(FamQuiz_R0_1AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext]; }
// Define qContext
NSManagedObjectContext *qContext = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"questions" inManagedObjectContext:qContext];
[fetchRequest setEntity:entity];
NSArray *fetchedObjects = [qContext executeFetchRequest:fetchRequest error:&error];
for (NSManagedObject *info in fetchedObjects) {
if ([[info valueForKey:#"idQ"] intValue] == questionNr) {
[read_Question addObject:[info valueForKey:#"question"]];
[read_Question addObject:[info valueForKey:#"qRightAnswer"]];
[read_Question addObject:[info valueForKey:#"qWrongAnswer1"]];
[read_Question addObject:[info valueForKey:#"qWrongAnswer2"]];
}
}
[fetchRequest release];
return [read_Question autorelease];
}

It seams that you are returning the object only inside the if statement. Meaning that if the if statement is false you will not autorelease the array. Or maybe you didn't paste the entire method. Let me know. Instruments is sometimes tricky.

This is a dupe of your other question Memory leak problem and i need help #1
When i did release i got into trouble,
of course. I did try to change the
names on the three and do release so
there was unique names but that did
not work.
Changing the names across three different files? That won't do anything and it indicates that you haven't entirely wrapped your head around objects, pointers, and memory management.
The Objective-C and Memory Management guides will help.
Could this be the reason for the leak
i have in this .m file?
Nope -- as I answered in the other question, the leak is most likely because you retain the object that is returned by that method and then don't release it anywhere.

Instruments is telling you were the leaked object was allocated, not where it was necessarily leaked.
While you may not be autoreleasing the array in all cases on return from that method, you might also be retaining it somewhere else and not balancing that retain with a release.

I am assuming you set the property managedObjectContext to "retain". Change the line to this (include "self" so that it gets retained):
if (self.managedObjectContext == nil) { self.managedObjectContext = [(FamQuiz_R0_1AppDelegate *)
[[UIApplication sharedApplication] delegate] managedObjectContext]; }
Then add your release back in.

Since I believe the code from picciano will fix the issue of the openingsposter, here a small explanation why it should fix the issue.
If you give a property the retain attribute, it will create an accessor method that looks somewhat like this (simplified):
#property (nonatomic, retain) NSValue *value;
- (void)setValue:(NSValue *)aValue {
value = [aValue retain];
}
Only when the retainCount reaches 0 an object is released, using retain, alloc and copy increases the retainCount. Remember: only when using the accessor method the retain actually happens (besides using alloc, retain and copy directly). The accessor method is usually called when using one of the following methods:
// the 2 most obvious ways to call the accessor methods ...
object.value = someValue;
[object setValue:someValue];
You created a retain property in your code, yet you didn't use the accessor method, so the object was never retained.
// no accessor used here ...
managedObjectContext = [(FamQuiz_R0_1AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
If you would release it from this point on, it would cause a crash, since the retainCount would actually become -1 at some point (since it never got to 1 in the first place). Therefore you should set the property like this:
// the dot-notation syntax to make use of the accessor method ...
self.managedObjectContext = [(FamQuiz_R0_1AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
or (in my opinion preferably):
// making use of the accessor method directly, which is very unambiguous ...
NSManagedObjectContext *context = [(FamQuiz_R0_1AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
[self setManagedObjectContext:context];
This way you can be sure the retain actually happens.
The second notation to accessor setters is in my opinion superior and I consider it good habit to use it for setting properties whenever possible. Read more about people who share this opinion and their reasoning on the following sites:
Cocoa Is My Girlfriend
The Big Nerd Ranch

Related

Bad Access on Core Data deleteObject

I could use some assistance in debugging a EXC_BAD_ACCESS error received on the [context deleteObject:loan]; command. The error is received in the following delegate method:
- (void)didCancelNewLoan:(Loan *)loan {
// save the context
NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
[context deleteObject:loan]; // *** EXC_BAD_ACCESS here ***
// This method is called from a the following method in a second class:
- (IBAction)cancel:(id)sender {
[delegate didCancelNewLoan:self.loan];
}
// The loan ivar is created by the original class
// in the below prepare for Segue method:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"NewLoan"]) {
UINavigationController *navController = (UINavigationController *)[segue destinationViewController];
LoanViewController *loanView = (LoanViewController *)[[navController viewControllers] lastObject];
loanView.managedObjectContext = self.managedObjectContext;
loanView.delegate = self;
loanView.loan = [self createNewLoan];
loanView.newLoan = YES;
}
// Finally, the loan is created in the above
// method's [self createNewLoan] command:
- (NSManagedObject *)createNewLoan {
//create a new instance of the entity managed by the fetched results controller
NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
NSEntityDescription *entity = [[self.fetchedResultsController fetchRequest] entity];
NSManagedObject *newManagedObject = [NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:context];
[newManagedObject setValue:[NSDate date] forKey:#"timeStamp"];
CFUUIDRef uuid = CFUUIDCreate(NULL);
CFStringRef uuidstring = CFUUIDCreateString(NULL, uuid);
//NSString *identifierValue = (__bridge_transfer NSString *)uuidstring;
[newManagedObject setValue:(__bridge_transfer NSString *)uuidstring forKey:#"identifier"];
CFRelease(uuid);
CFRelease(uuidstring);
NSError *error;
[self.fetchedResultsController performFetch:&error];
NSLog(#"%i items in database", [[self.fetchedResultsController fetchedObjects] count]);
return newManagedObject;
}
Appreciate your looking at the above methods.
Guess #1: you are accessing a deallocated object. To debug: turn on zombies and see what happens.
Update: here's how you turn on zombies in Xcode 5:
Product > Scheme > Edit Scheme, select Diagnostics tab, check "Enable Zombie Objects"
for older Xcode
, edit your build settings, add and enable these arguments in your build scheme:
Guess #2: you have a multithreaded app and you are accessing a managed object context from different threads, which is a no no.
You can add an assert before your delete:
assert( [ NSThread isMainThread ] ) ;
From looking at your code above, there's nothing that stands out as being done incorrectly.
I am wondering whether you are dealing with two different managed object contexts without realising it? You will have to set some breakpoints where you create the Loan object and see if that might be the case.
Also why do you have to get a reference to the context via fetchedResultsController if you already have a declared property for it in self.managedObjectContext ?
The other thing is why do you need to call the fetchedResultsController to performFetch: again when you create a new Loan object? Is your data presented in a table view and have you implemented the NSFetchedResultsController delegate methods?
That call seems unnecessary and it may be causing issues with the cache created by the fetch. See section "Modifying the fetch request" under this link http://developer.apple.com/library/ios/documentation/CoreData/Reference/NSFetchedResultsController_Class/Reference/Reference.html#//apple_ref/doc/uid/TP40008227-CH1-SW24
Finally, try your delete operation directly in the view controller that received the action rather than pass it to the delegate (just to eliminate the possibility that something has been dealloc'd without you knowing).
Here's what I'd do:
- (IBAction)cancel:(id)sender
{
NSError *error;
NSManagedObjectContext *context = [self.loan managedObjectContext];
[context deleteObject:self.loan];
if (![context save:&error])
NSLog (#"Error saving context: %#", error);
}
I got a Bad Access because a deallocated UIViewController was a delegate of a NSFetchedResultsController it had.
The NSFetchedResultsController was deallocated - but when settings a delegate, it observes NSManagedObjectContext for changes, so when NSManagedObjectContext was saved - a bad access would occur when trying to notify the NSFetchedResultsController about the change.
Solution is to clear delegate of NSFetchedResultsController upon deallocation.
- (void)dealloc {
fetchedResultsController.delegate = nil;
}

How to pass ManagedObjectContext to TabBarViewControllers

After reading quite a couple of questions in stackoverflow, I still can't find an answer at the moment.
I have trouble passing ManagedObjectContext from appdelegate to my tabbarcontroller view.
in my appdelegate.m I have this
#import "memoView.h"
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
memoView *mView = (memoView *)navigationController.topViewController;
mView.ObjectContext =[self managedObjectContext];
}
in memoView.m
- (void)viewDidLoad
{
[super viewDidLoad];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"NoteLog" inManagedObjectContext:[self ObjectContext]];
[fetchRequest setEntity:entity];
NSError *error;
self.memoInfo = [ObjectContext executeFetchRequest:fetchRequest error:&error];
//self.title = #"Memo";
[fetchRequest release];
}
error reason: '+entityForName: could not locate an NSManagedObjectModel for entity name 'NoteLog''
I'm not sure what I have done wrong, I'm relative new to core data.
Any comments are appreciated.
I ran into this at one point and solved like this:
YourAppDelegate *appDelegate = (YourAppDelegate *)[[UIApplication sharedApplication] delegate];
self. ObjectContext = appDelegate.ManagedObjectContect;
Where do you put following code? I guess mView is nil when you assigning mView.ObjectContext:
#import "memoView.h"
memoView *mView = (memoView *)navigationController.topViewController;
mView.ObjectContext =[self managedObjectContext];
I'd personally implement a Singleton for my CoreData Stack. - But in a slightly safer manner like here.
The singleton pattern is basically an excuse for a "global variable". Although if implemented correctly and used wisely one of the most powerful patterns there are. Simply speaking, what it does is: It creates an object of a class IF it doesn't exist yet and stores it in a static variable of that object's class. And since a class variable's content is the same amongst all the instances of that class (obviously), the next time you try to allocate an instance of the class it checks whether it was allocated before, and if it was returns the old instance.
Thus, you can basically "allocate" from anywhere and however often you want and you'll always get the same object back.
People tend to then call that singleton class something along the lines of "DataManager" or similar.

Memory leak problem, could someone explain what this mean?

I am getting a memory leak on the following code line:
The code-line:
NSArray *fetchedObjects = [qContext executeFetchRequest:fetchRequest error:&error];
I have been trying to read-up on this and have tried to find the cause of this for quite some time, without success.
Could someone give me a hint where to look? ...and do i understand the "100%" correctly that it indicate that this code line is 100% causing the leak?
UPDATE
Some more code:
//=========PREPARE CORE DATA DB===========//
if (managedObjectContext == nil) { managedObjectContext = [(FamQuiz_R0_1AppDelegate *)
[[UIApplication sharedApplication] delegate] managedObjectContext]; }
// Define qContext
NSManagedObjectContext *qContext = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"questions" inManagedObjectContext:qContext];
[fetchRequest setEntity:entity];
NSArray *fetchedObjects = [qContext executeFetchRequest:fetchRequest error:&error];
for (NSManagedObject *info in fetchedObjects) {
if ([[info valueForKey:#"qDiff"] intValue] == 1) {
[allEasyArrayQ addObject:[info valueForKey:#"idQ"]];
} else if ([[info valueForKey:#"qDiff"] intValue] == 2) {
[allMediumArrayQ addObject:[info valueForKey:#"idQ"]];
} else if ([[info valueForKey:#"qDiff"] intValue] == 3) {
[allHardArrayQ addObject:[info valueForKey:#"idQ"]];
}
}
You are probably over-retaining the array or the objects inside it later in this method or even outside of this method. The line will only indicate where the objects are created, not where the actual extra retain is performed. For that you'd need to check using Instruments which call stacks retain and release the leaked objects.
No, the 100% means that this leak accounts for 100% of your leaked memory (ie, this is you only leak).
Note if you want to fix the leak you are looking in the wrong place. Leaks tells you which instance is leaking, it's Class and it's memory management history. Try a tutorial like this one http://www.cimgf.com/2008/04/02/cocoa-tutorial-fixing-memory-leaks-with-instruments/
Note, what Leaks can't tell you is the line where you are leaking, as the cause of your leak is something you haven't done, as opposed to something you have done (ie you didn't release your object).

Memory leak problem and i need help #1

I am very new at this and seems to have a leak in this piece of code that i cannot fix:
The Instruments shows on this line with a 100%:
NSMutableArray *read_Question = [[NSMutableArray alloc] initWithCapacity: 0];
I have tried all kind of things but not been able to fix it.
Anyone nice that can advice me how to proceed?
- (NSMutableArray *)readQuestion: (int)questionNr {
NSMutableArray *read_Question = [[NSMutableArray alloc] initWithCapacity: 0];
NSError *error;
//=========PREPARE CORE DATA DB===========//
if (managedObjectContext == nil) {
managedObjectContext = [(FamQuiz_R0_1AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext]; }
// Define qContext
NSManagedObjectContext *qContext = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"questions" inManagedObjectContext:qContext];
[fetchRequest setEntity:entity];
NSArray *fetchedObjects = [qContext executeFetchRequest:fetchRequest error:&error];
for (NSManagedObject *info in fetchedObjects) {
if ([[info valueForKey:#"idQ"] intValue] == questionNr) {
[read_Question addObject:[info valueForKey:#"question"]];
[read_Question addObject:[info valueForKey:#"qRightAnswer"]];
[read_Question addObject:[info valueForKey:#"qWrongAnswer1"]];
[read_Question addObject:[info valueForKey:#"qWrongAnswer2"]];
}
}
[fetchRequest release];
return [read_Question autorelease];
}
It seams that you are returning the object only inside the if statement. Meaning that if the if statement is false you will not autorelease the array. Or maybe you didn't paste the entire method. Let me know. Instruments is sometimes tricky.
This is a dupe of your other question Memory leak problem and i need help #1
When i did release i got into trouble,
of course. I did try to change the
names on the three and do release so
there was unique names but that did
not work.
Changing the names across three different files? That won't do anything and it indicates that you haven't entirely wrapped your head around objects, pointers, and memory management.
The Objective-C and Memory Management guides will help.
Could this be the reason for the leak
i have in this .m file?
Nope -- as I answered in the other question, the leak is most likely because you retain the object that is returned by that method and then don't release it anywhere.
Instruments is telling you were the leaked object was allocated, not where it was necessarily leaked.
While you may not be autoreleasing the array in all cases on return from that method, you might also be retaining it somewhere else and not balancing that retain with a release.
I am assuming you set the property managedObjectContext to "retain". Change the line to this (include "self" so that it gets retained):
if (self.managedObjectContext == nil) { self.managedObjectContext = [(FamQuiz_R0_1AppDelegate *)
[[UIApplication sharedApplication] delegate] managedObjectContext]; }
Then add your release back in.
Since I believe the code from picciano will fix the issue of the openingsposter, here a small explanation why it should fix the issue.
If you give a property the retain attribute, it will create an accessor method that looks somewhat like this (simplified):
#property (nonatomic, retain) NSValue *value;
- (void)setValue:(NSValue *)aValue {
value = [aValue retain];
}
Only when the retainCount reaches 0 an object is released, using retain, alloc and copy increases the retainCount. Remember: only when using the accessor method the retain actually happens (besides using alloc, retain and copy directly). The accessor method is usually called when using one of the following methods:
// the 2 most obvious ways to call the accessor methods ...
object.value = someValue;
[object setValue:someValue];
You created a retain property in your code, yet you didn't use the accessor method, so the object was never retained.
// no accessor used here ...
managedObjectContext = [(FamQuiz_R0_1AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
If you would release it from this point on, it would cause a crash, since the retainCount would actually become -1 at some point (since it never got to 1 in the first place). Therefore you should set the property like this:
// the dot-notation syntax to make use of the accessor method ...
self.managedObjectContext = [(FamQuiz_R0_1AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
or (in my opinion preferably):
// making use of the accessor method directly, which is very unambiguous ...
NSManagedObjectContext *context = [(FamQuiz_R0_1AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
[self setManagedObjectContext:context];
This way you can be sure the retain actually happens.
The second notation to accessor setters is in my opinion superior and I consider it good habit to use it for setting properties whenever possible. Read more about people who share this opinion and their reasoning on the following sites:
Cocoa Is My Girlfriend
The Big Nerd Ranch

Memory leak in cocoa touch with a mutableCopy

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];