Releasing NSMutableArray in dealloc causes the app to crash - ios

I have a UIView which holds a UITableView. This table is being populated by an NSMutableArray.
I initialized both the array and the table in the UIView's initWithFrame: method.
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
bookTable = [[UITableView alloc]initWithFrame:CGRectMake(100, 108, 260, 535)];
bookArray = [[NSMutableArray alloc]init]; //this array will be populated later
//other codes
}
return self;
}
I release them in dealloc:
- (void)dealloc
{
[bookTable release];
[bookArray release];
[super dealloc];
}
The problem is when the app crashes in dealloc. It produces the error: [CALayer release]: message sent to deallocated instance 0x82db660
I looked everywhere in my code and I'm sure that I didn't overreleased anything. I decided to remove the [bookArray release] in dealloc, and it stopped crashing. I ran the Analyze and it doesn't give me any potential leaks.
Can anyone explain to me why releasing the array in dealloc causes the crash?
NOTE:
Both the table and the array are instance variables of the view.
EDIT:
Code for populating the array:
NSString *sql = [NSString stringWithFormat:#"SELECT BookName FROM books WHERE UserID=%d", userId];
booksArray = [sqlite executeQuery:sql];
Please note that I am using a sql wrapper.

Here lies the problem
booksArray = [sqlite executeQuery:sql];
The array returned by the sqlite is an autoreleased object. You must either retain it or remove release from `dealloc'.
booksArray = [[sqlite executeQuery:sql] retain];
Since it is autoreleased object, releasing it in dealloc results in crash when system also tries to release it.
And do remove the array initialization in init method, it's redundant.

Problem is here only, I assume.
booksArray = [sqlite executeQuery:sql];
the executeQuery method might be returning an autoreleased NSMutableArray or something like this might be written inside executeQuery
NSMutableArray* tempArray = [NSMutableArray arrayWithCapacity:0];
If its like this, it should already be in autoreleased mode, hence no release in dealloc method.
UPDATE
Also, as0sign property to the array.
#property (nonatomic, retain) NSMutableArray* booksArray;
And then, wherever you use it, use it as self.booksArray
self.booksArray = [sqlite executeQuery:sql];
This way, it would be proper memory management and then release the array in dealloc.

Change the return statement of your executeQuery method like [array autorelease]
Replace booksArray = [sqlite executeQuery:sql]; by booksArray = [[sqlite executeQuery:sql] retain];
If you wrote property for the booksArray like #property(nonatomic, retain) NSMutableArray *bookArray; then you can replace step 2 by self.bookArray = [sqlite executeQuery:sql];
Your issue is you are returning an released object from the executeQuery method (You mentioned it on your comment). That's why you are getting the error

I would suggest to create a property for the booksArray like this:
#property (nonatomic, retain) NSMutableArray *bookArray;
An when you populate it try this code:
self.bookArray = [NSMutableArray arrayWithArray:[sqlite executeQuery:sql]];
That'll create an autoreleased copy of the returned array which is then "auto"-retained by the property.
Of course you should make sure, that the array return by [sqlite executeQuery:sql] is autoreleased.

Related

ARC two-way relationship

So I want to have multiple nodes that are connected. Every node has outgoing and incoming connections. But the NSMutableArrays are creating leaks although i'm using ARC. How can i get the objects to be released properly? I'm already using an autoreleasepool.
The code so far is:
#interface TestObj()
#property(strong) NSMutableArray *incoming;
#property(strong) NSMutableArray *outgoing;
#end
#implementation TestObj
#synthesize incoming,outgoing;
- (id)init
{
self = [super init];
if (self) {
incoming = [NSMutableArray array];
outgoing = [NSMutableArray array];
}
return self;
}
-(void)addIncoming:(TestObj *)incomingN {
if([incoming indexOfObject:incomingN] == NSNotFound) {
[incoming addObject:incomingN];
[incomingN addOutgoing:self];
}
}
-(void)addOutgoing:(TestObj *)outgoingN {
if([outgoing indexOfObject:outgoingN] == NSNotFound) {
[outgoing addObject:outgoingN];
[outgoingN addIncoming:self];
}
}
With ARC, as with manual memory management on iOS, you need to avoid retain cycles. If you have one object that is retaining a second, and the second is retaining the first, those two will never be deallocated even if nothing else references them, so you have a memory leak.
You need to make it so that you aren't referencing them like this. NSArray and NSMutableArray keep strong references to other objects. You can do something like the following to create a weak reference that you can story in the array:
NSValue *val = [NSValue valueWithNonretainedObject:object];
If you store val in the array, it won't have a strong reference to the object, so it can be deallocated. However, you have to be careful that you aren't creating a situation where some of your objects have no strong references, or they will get deallocated before you want them to.
Well this may sound basic, but have tried setting them = to nil?

iOS - XCode 4.4 - Potential Memory Leaks Using Analyze

I ran analyze on my 1st iPhone app, and I see a few potential memory leaks. The app itself works fine on the simulator.
I would like to do the right thing and clear the potential memory leaks, but some are quite puzzling. Maybe someone could help me here?
Thanks in advance.
Pier.
Error 1) The Analyzer says "Potential leak of an object stored in tempDate and tempAns"
#import "Answer.h"
#implementation Answer
#synthesize answerTiming;
#synthesize xPosition;
#synthesize earlyOrLate;
#synthesize hit;
+ (Answer *) createAnswerWithTiming :(NSDate *)paramTiming andXPosition :(float) xPosition
{
NSDate * tempDate = [[NSDate alloc] initWithTimeInterval:0 sinceDate:paramTiming];
Answer * tempAns = [[Answer alloc] init ];
[tempAns setAnswerTiming:tempDate];
[tempDate release];
[tempAns setXPosition:xPosition];
[tempAns setEarlyOrLate:0];
[tempAns setHit:false];
return tempAns;
}
- (void)dealloc {
[answerTiming release];
[self release];
[super dealloc];
}
#end
Error 2) Analyzer says (see below)
- (void)viewDidLoad
{
[super viewDidLoad];
........
...
UIImage * perfectImage = [UIImage imageNamed: #"perfect.png"];
self.perfectImageView2 = [[UIImageView alloc]initWithImage:perfectImage];
// method returns an objective C content with a +1 retain count
[self.perfectImageView2 setFrame:CGRectMake(145.0f,
150.0f,
self.perfectImageView2.image.size.width,
self.perfectImageView2.image.size.height)];
self.view.backgroundColor = [UIColor whiteColor];
UIImage * levelUpImage = [UIImage imageNamed:#"levelup.png"];
self.levelUpImageView = [[UIImageView alloc] initWithImage:levelUpImage];
[self.levelUpImageView setFrame:CGRectMake(100.0f,
400.0f,
self.levelUpImageView.image.size.width,
self.levelUpImageView.image.size.height)];
//object leaked, allocated object is not referenced later in this execution path and has a retain count of +1
self.view.backgroundColor = [UIColor whiteColor];
}
Error 3)
- (NSMutableArray *) generateQuestionTapAnswers:(NSString *) answersString withFirstBeat: (NSDate *) firstBeatTime
{
NSArray * notesToDraw = [answersString componentsSeparatedByCharactersInSet: [NSCharacterSet characterSetWithCharactersInString: #" "]];
float noteValueOffset = 0.0;
NSMutableArray * answerArray = [[NSMutableArray alloc] init ];
// Method returns an objective C object with a +1 retain count
for (int i=1; i < notesToDraw.count; i++) // i = 0 is the time signature
{
.....
}
return answerArray;
// Object returned to caller as an owning reference (single retain count transferred to caller)
// Object leaked: Object allocated and stored into answerArray is returned from a method whose name generateQuestionTapAnswers does not start with copy, mutableCopy
}
Regarding your first and third warnings, the compiler will assume that you'll be creating an object in "a method whose name begins with alloc, new, copy, or mutableCopy" (see Advanced Memory Management Programming Guide). So, if you're returning a +1 object, make sure your method name begins with one of those four prefixes. If you create a +1 object without one of those prefixes, it won't be happy. So, for error #1, if you rename that method to be something like newAnswerWithTiming, that warning should go away. (If the calling method doesn't clean up after them, though, the warning will just be shifted to that routine, but let's take it one step at a time.)
Likewise for error #3, if you rename that method to be something like newAnswerArrayFromQuestionTapAnswers (or whatever ... I'm not sure if I understand your method name ... just make sure it starts with new), you'll similarly eliminate that warning. In both cases, just make sure to release it at the appropriate point because whomever called these two respective methods now "owns" those objects and is responsible for their cleanup.
As an aside, you don't need to do [self release] in your dealloc in your discussion of error #1. (Frankly, I'm surprised it doesn't generate an error.) You don't need it because if self wasn't already at retainCount of zero, you never would have gotten to dealloc in the first place.
Regarding error #2, how are the properties perfectImageView2 and levelUpImageView defined? If retain or strong, you're creating something with a +2 retainCount, and you probably want to add an autorelease after the alloc/init.

ios alloc-release

My app is receiving memory warnings because it's asking for lot of memory. I try to release every allocation. However, sometimes I don't know how to do it.
For example: I have two pairs of .h and .m file. One of them makes connections with a server and the other with local SQLite.
Usually, the code which calls to a method from those files are like this:
-(NSMutableArray *) getRecentActivity{
LocalStorageController *local = [[LocalStorageController alloc]init];
return [local getRecentActivity];
}
getRecentActivity returns a NSMutableArray.
Well, in that piece of code we can see that I am allocating memory for the LocalStorageController but I never call to the release method so, I suppose, the more I call that function, the more memory I will be allocating.
If I call autorelease after init, it will crash.
Moreover, usually, I use this other kind of code:
ServerConnection *serv = [[ServerConnection alloc]init];
NSMutableArray list = [serv getMyListOfContacts];
Which uses ASIHTTPRequest and, if I call [serv release]; after the second line, the app crashes with EXC_BAD_ACCESS pointing to a line in ASIHTTPRequest library.
How is suppose to manage this situation?
Thank you very much!
The first case is easy;
-(NSMutableArray *) getRecentActivity{
LocalStorageController *local = [[LocalStorageController alloc]init];
NSMutableArray *tmp = [local getRecentActivity];
[local release];
return tmp;
}
The second case is hard to solve in a general way without seeing more of the actual code.
Using serv as a property would be fixing this retain/release problem.
In your .h:
#property (nonatomic, retain) ServerConnection *server;
In your .m:
#synthesize server;
- (void)dealloc {
[server release];
// The rest of your releases here...
[super dealloc];
}
- (void)yourMethod {
ServerConnection *myServConnection = [[ServerConnection alloc] init];
self.serv = myServConnection;
[myServConnection release];
NSMutableArray list = [self.serv getMyListOfContacts];
}
Just keep on using self.serv in this class from that point on and you won't have a problem with having the object being released.

Can an autoreleased NSArray cause a memory leak?

I am trying to create an NSMutableArray using arrayWithArray, add two objects, sort, and store to an ivar as an NSArray. My code looks like this:
NSMutableArray *_mutableItems = [NSMutableArray arrayWithArray:[self.mainViewController.someDictionary allKeys]];
[_mutableItems addObject:#"Buildings"];
[_mutableItems addObject:#"Parking"];
self.curItems = [_mutableItems sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
When I profile the app I get a memory leak for an NSArray after the view is popped. But what I don't understand is: aren't all of these objects autoreleased? Am I increasing the retain count when I assign it to the instance property?
Yes, setting the property is probably increasing the retain count. Specifically, _mutableItems will be autoreleased, but the array you create with sortedArrayUsingSelectoris retained by the property.
Does your property declaration include retain or copy?
#property (retain) NSArray *curItems;
If so, in your class dealloc method, make sure you call release on the array;
- (void)dealloc {
[curItems release];
[super dealloc];
}

NSMutableDictionary memory leak - how do I fix it without crashing the App?

I must have misunderstood some of the memory management rules, because when I try to fix a memory leak, the App crashes. Let me show you some code:
calendarRequestLog is a property of type MutableDictionary in a singleton object, that exists as long as the App runs. Here's the declaration in the .h file:
#property (nonatomic, retain, readonly) NSMutableDictionary *calendarRequestLog;
I allocate it with (in init):
calendarRequestLog = [[NSMutableDictionary alloc] init];
I fill it with this (notice the retain, that creates the memory leak):
[calendarRequestLog setObject:[[NSMutableArray arrayWithObject:delegate] retain] forKey:date];
I sometimes access it with this:
NSMutableArray* delegates = [calendarRequestLog objectForKey:date];
if(delegates != nil) {
// add delegates
}
I empty it with this:
NSMutableArray* delegates = [calendarRequestLog objectForKey:date];
if(delegates != nil) {
for (id <ServerCallDelegate> delegate in delegates) { … }
// clear the request from the log
[calendarRequestLog removeObjectForKey:date];
}
Here's the code that crashes when I remove the retain above:
NSMutableArray* delegates = [calendarRequestLog objectForKey:date];
if(delegates != nil) {
if([delegates containsObject:delegate]) // crash
[delegates removeObject:delegate];
}
It crashes because delegates is deallocated but not nil. To be more precise, I get an EXC_BAD_ACCESS Exception.
All these methods may be called in different orders or multiple times.
I cannot figure out, why this happens. I thought, collections are supposed to retain their objects - as this array-object (delegates) is still in the collection, it should not be deallocated. Other code cannot be responsible, I showed you all occurrences of calendarRequestLog.
I appreciate all the help I can get!
#Edit
I think I got it.
I call the crashing method when the delegate gets deallocated, so that I do not call the delegate per accident later.
But: I retain the delegates in my calendarRequestLog, so it cannot get deallocated as long as this doesn't get called:
// clear the request from the log
[calendarRequestLog removeObjectForKey:date];
...which in turn, deallocates the delegate and calls the crashing method. As the calendarRequestLog has removed the delegates, but not yet the key, we crash.
Ok, I will solve this differently. Thanks for all the comments - thanks to you, I looked elsewhere!
Did you try retaining when fetching so nobody releases your object while you're using it?
NSMutableArray* delegates = [[calendarRequestLog objectForKey:date] retain];
if(delegates != nil) {
if([delegates containsObject:delegate]) // crash
[delegates removeObject:delegate];
}
[delegates release];
Common practice is the following, because you already retain in the .h file:
//create local instance, then copy that to the class wide var
NSMutableDictionary *_calendarRequestLog = [NSMutableDictionary alloc] init];
self.calendarRequestLog = _calendarRequestLog;
[_calendarRequestLog release];
Also, I don't really understand why you would retain here:
[calendarRequestLog setObject:[[NSMutableArray arrayWithObject:delegate] retain] forKey:date];
Why not just change that to:
[calendarRequestLog setObject:[NSMutableArray arrayWithObject:delegate] forKey:date];
Write instead
calendarRequestLog = [[NSMutableDictionary alloc] init];
this
self.calendarRequestLog = [NSMutableDictionary dictionary];
and try to use property instead ivar

Resources