In my iOS app, I am using a NSMutableArray, named imageMArray. I have set its getter and setter properties and instantiated it.
In viewDidLoad:
imageMArray=[[NSMutableArray alloc] initWithArray:CategoryImages];
imageMArray=[self shuffleOnlyArray:imageMArray];
In ShuffleOnlyArray Method:
NSMutableArray *destArray1 = [[NSMutableArray alloc] initWithCapacity: [sourceArray count]] ;
return destArray1;
In shuffle Method:
imageMArray=[[self shuffleOnlyArray:imageMArray] retain];
There appears to be a memory leak in the Shuffle method.
Should I release imageMArray or set it to nil? If it should be released, should it be autoreleased?
imageMArray=[[NSMutableArray alloc] initWithArray:CategoryImages];
In the above statement, you have a memoryleak.
Instead you can have like as follows.
imageMArray = [NSMutableArray arrayWithArray:CategoryImages];
In ShuffleOnlyArray Method, return the autoreleased object.
NSMutableArray *destArray1 = [[NSMutableArray alloc] initWithCapacity: [sourceArray count]] ;
return [destArray1 autorelease];
But after you get it, retain (take the ownership) the array object.
imageMArray=[[self shuffleOnlyArray:imageMArray] retain];
Edit
In shuffle method, do as follows:
NSMutableArray *imageMArray1 = [imageMArray mutableCopy];
if( imageMArray )
{
[imageMArray release];
}
imageMArray=[[self shuffleOnlyArray:imageMArray1] retain];
[imageMArray1 release];
Edit 2:
One more solution:
Use the category to shuffle as mentioned in the SO link
No need of creating new and releasing the arrays.
1 You already have a memory leak in the following lines.
imageMArray = [[NSMutableArray alloc] initWithArray:CategoryImages];
imageMArray = [self shuffleOnlyArray:imageMArray];
In the first line you create an object with retain count 1.
Then you say that your imageMArray pointer points to other object. You should release the first object, because you louse the reference to the fist object and you can not release it after you change the reference!
2 You should not use retain because your ShuffleOnlyArray method returns a retained object.
Your factory method should return an autorelease object and the caller of the factory should decide if if will retain it or not.
Hope I was clear enough
Related
I have an NSMutableArray with the following property:
#property (nonatomic, strong) NSMutableArray *alarmTableArray;
alarmTableArray = [[NSMutableArray alloc]init];
FMDBDatabaseAccess *db = [[FMDBDatabaseAccess alloc] init];
alarmTableArray = [db getAlarm];
I tried releasing this array but I end up with EXC_BAD_ACCESS.
I am really worried about this.
How to release this array?
You're using the descriptor of "strong" which is an ARC term. This should be retain and if you just set the property to nil it will release it automatically. You should set it to nil in your viewDidUnload since your ViewWillDissappear only means your viewcontroller is leaving visibility and not that it is being destroyed.
Updated Answer
I think I know what you're trying to do. You want grab an array of rows from your SQL and store it in one of your array.
One of the techniques for getting rows of data from SQL and storing into a class instance variable array is to NOT return a temporary array but pass the class instance variable array as a reference into your method and modify the array directly.
So instead of this pseudo-code
-(NSMutableArray *)doSomething
{
NSMutableArray *tempArray;
while (DB select statement has found rows)
{
CockTail *objCT = [[CockTail alloc] init];
objCT.name = #"...";
objCT.price = #"...";
[tempArray addObject:objCT];
[objCT release];
}
return [tempArray autorelease];
}
// class instance variable array
instanceVarArray = [[NSMutableArray alloc] init];
instanceVarArray = [self doSomething]; // here is where you confusion arise
You can do it this way:
-(void)doSomething:(NSMutableArray *)paramArray
{
// remove previously fetched data
[paramArray removeAllObjects];
SQL select statement
while(has rows)
{
CockTail *objCT = [[CockTail alloc] init];
objCT.name = #"...";
objCT.price = #"...";
// NOTE: we are directly modifying our class instance variable array
// here since it was passed by reference :D
// and so there is no need to worry about releasing the array
[paramArray addObject:objCT];
[objCT release];
}
}
// Now all you do is pass in your class instance variable array
instanceVarArray = [[NSMutableArray alloc] init];
[self doSomething:instanceVarArray];
Original Answer
Um, maybe I am wrong but aren't you essentially throwing away that "alloc init" on the first line here when you assign the array something from your FMDBDatabaseAccess:
// LINE 1: this instance of NSMutableArray here is allocated
alarmTableArray = [[NSMutableArray alloc]init];
// LINE 2
FMDBDatabaseAccess *db = [[FMDBDatabaseAccess alloc] init];
// LINE 3:this line here essential breaks the pointer link point to the NSMutableArray instance on line 1
alarmTableArray = [db getAlarm];
Now unless you do
// LINE 4
[alarmTableArray retain];
Otherwise, your alarmTableArray was never allocated (since you overwrote the pointer link). And as a result, you've caused a memory leak as your profiler told you.
Doing a release now would give your that EXEC_BAD_ACCESS
What I think you want to do is this:
alarmTableArray = [[NSMutableArray alloc]init];
FMDBDatabaseAccess *db = [[FMDBDatabaseAccess alloc] init];
// this now uses the setter method (mutator method generated by #property) to do the copy
self.alarmTableArray = [db getAlarm];
Looking at your while loop, I have to ask why are you releasing a local scope variable?
CockTail *cocktailValues = [[CockTail alloc] init];
...
[cocktails addObject:cocktailValues];
[cocktailValues release];
Breakdown of each line of code above:
When you alloc and init the CockTail object the release/retain count is 0.
Adding the object to the NSMutableArray increases the release/retain count to 1.
Releasing the CockTail object after you added it to array reduce the release/retain count back down to 0.
Therefore, later when you release the NSMutableArray or try to access an object in it, the objects are already gone.
Remember the number one rule, only release what you retain.
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Use autorelease when setting a retain property using dot syntax?
What is difference between using ivars and self. notation?
instanceVar is instance variable declared with retain.
1) instanceVar = [[NSMutableArray alloc] initWithObjects:#"1", #"2"]; //do I need autorelease here?????
2) self.instanceVar = [[NSMutableArray alloc] initWithObjects:#"1", #"2"] autorelease];
Also, Do I need autorelease in the first situation?
This is explained in multiple places but seems as you asked what the different is
The first call is unchanged and looks like this:
instanceVar = [[NSMutableArray alloc] initWithObjects:#"1", #"2"];
The second call when compiled will look something like this (assuming you have used a #property with retain and #synthesize:
self.instanceVar = [[NSMutableArray alloc] initWithObjects:#"1", #"2"];
// The previous line will compile to this next line
[self setInstanceVar:[[NSMutableArray alloc] initWithObjects:#"1", #"2"]];
The body of the - (void)setInstanceVar:(NSMutableArray *)instanceVar; method will look something like this (the compiler create this for you because of your #property and #sythesize):
- (void)setInstanceVar:(NSMutableArray *)anInstanceVar
{
if (instanceVar != anInstanceVar) {
[instanceVar release];
instanceVar = [anInstanceVar retain];
}
}
Therefore in the call
self.instanceVar = [[NSMutableArray alloc] initWithObjects:#"1", #"2"];
You have the +1 retain count on the newly created NSMutableArray and then you have the +1 retain count added from going through the setter.
This means that you require the extra release to match retains you are taking. It is considered better to not use autorelease in iPhone so you can be sure memory is being freed when you want it to. Therefore you should normally take the pattern
Create local var
Assign local var to ivar through setter
release local var
Which looks like this (FIXED thanks to #jamapag)
NSArray *tmpMyArray - [[NSArray alloc] initWithObject:#"Hello"];
self.myArray = tmpMyArray;
[tmpMyArray release]; tmpMyArray = nil;
1) instanceVar = [[NSMutableArray alloc] initWithObjects:#"1", #"2"]; //do I need autorelease here?????
The NSmutableArray is created with a retain count of 1, you need to release your instanceVar in your dealloc() method
2) self.instanceVar = [[NSMutableArray alloc] initWithObjects:#"1", #"2"] autorelease];
Here you are using the setter, and since it is declared with retain it will increase its retain count by 1, the alloc init already increased the retain count by 1, so the total retain count is 2. However the autorelease msg will decrease this by 1 probaby in the next run loop. So again you only have to release this on your dealloc() method.
In the first situation you probably DO NOT want to autorelease, since this is an IVar you will probably want to use it again, and if you autorelease it the retain count will be 0 soon (most likely in the next run loop)
I have been pulling out my hair trying to figure out why this is leaking. In my .h file I have a synthesized property nonatomic, retained NSMutableArray. In my viewDidLoad I declare it as:
self.tableData = [[NSMutableArray alloc] init];
[self.tableData removeAllObjects];
fillData(self.tableData);
Throughout my application, I call [self.tableData removeAllObjects] and then repopulate it with the fillData(self.tableData) function. This function fills up the data from a static C++ string set:
void fillData(NSMutableArray* list)
{
for (set<string>::const_iterator itr = sortedData.begin(); itr != sortedData.end(); ++itr){
[list addObject:[NSString stringWithFormat:#"%s", ((string)*itr).c_str()]];
}
}
In my dealloc method I do:
[self.tableData removeAllObjects], [self.tableData release], tableData = nil;
Where did I drop the ball? Instruments says it's in the [list addObject....] line.
Thanks
self.tableData = [[NSMutableArray alloc] init];
[self.tableData removeAllObjects];
fillData(self.tableData);
+1 retain for alloc, +1 retain for using the property's setter. You haven't balanced the +1 from alloc. If you are going to use the setter:
self.tableData = [NSMutableArray array];
fillData(self.tableData);
Note that removeAllObjects in that is completely pointless.
This is odd, too:
[self.tableData removeAllObjects], [self.tableData release], tableData = nil;
First, don't bother removing the objects. When the array is deallocated, it'll release all objects. Secondly, using the setter to call release and then immediately do a direct assignment is inconsistent. Either do:
self.tableData = nil;
Or:
[tableData release], tableData = nil;
(Note that the use of the , in all of this is also purely for your benefit -- it has no impact on generated code.)
Also, use stringWithUTF8String: and not stringWithFormat:.
Not sure if it's the leak, but this looks like it's a problem:
self.tableData = [[NSMutableArray alloc] init];
You say that tableData is a property that's retained. Try:
self.tableData = [NSMutableArray arrayWithCapacity:10];
That way the property retains it and the array itself is autoreleased. Your release in dealloc will bring the retain count back down to zero.
The problem is that your property is set as retain, and you set it to an already retained object.
You should do it like this:
// viewDidLoad
NSMutableArray *array = [[NSMutableArray alloc] init];
self.tableData = array;
[array release]; // this is important
// dealloc
self.tableData = nil; // will automatically release the array
In your dealloc, you use properties which retain the tableData again. That is not really what you want, so do:
[tableData release];
or
[self->tableData release]; // not necessary, but some prefer it.
or
self.tableData = nil; // property will handle release
No need to clear the tableData, no need to set anything to nil (you are deallocating, so nothing will access it anymore).
I want to get some memory leaks in my code, how can i fix the memory leak.
dashboard = [[NSMutableArray alloc] init];
[dashboard addObject:[[NSDictionary alloc] initWithObjectsAndKeys:#"demo_1.jpg",#"pic_source",#" Head",#"Title",nil]; // memory leaks here.
if ( theConnection ) {
receiveData = [[NSMutableData data] retain]; //memory leaks here.
}
But i have released dealloc - in[receiveData release];, but memory leaks happened. I know the retain, the count is increased, but how can i released the data properly.
Thanks!
i think your leak in the line:
[dashboard addObject:[[NSDictionary alloc] initWithObjectsAndKeys:#"demo_1.jpg",#"pic_source",#" Head",#"Title",nil];
just change it to
[dashboard addObject:[NSDictionary dictionaryWithObjectsAndKeys:#"demo_1.jpg",#"pic_source",#" Head",#"Title",nil];
addObject retains the object so you can use convenience creation methods which autorelease the object they create.
and another possible leak... if you define your receiveData as a property with retain attribute you don't need to call retain explicitely. You can call self.recieveData = [NSMutableData data]. This will retain it. Of course you will still need to release it in dealloc.
EDIT to show the code:
NSMutableArray *anArray = [[[NSMutableArray alloc] init] autorelease];
[sections setValue:anArray forKey:display_date];
dashboard addObject:[[NSDictionary alloc] initWithObjectsAndKeys:#"demo_1.jpg",#"pic_source",#" Head",#"Title",nil];
You've called init method - then you are the owner of an object. When you've put it to the array - the retain was called too. Just call autorelease to fix the leak.
receiveData = [[NSMutableData data] retain]; - if you will call release this object will be deleted when out of scope. If you will write
receiveData = [NSMutableData data];
It will be automatically deleted when out of scope
EDIT
If you are using methods with init word in them then you are the owner of such objects and they will not be deleted automatically until you'll call release method on them. If you don't want to own the object create it with some static method. For example:
NSMutableArray *array = [NSMutableArray arrayWithObjects: ... , nil];
It's the same as calling
NSMutableArray *array = [[[NSMutableArray alloc] initWithObjects: ... , nil] autorelease];
Autorelease means the object will receive a release method when out of scope - and if it was not retained will be deleted automatically
I'm using the following method in my code:
- (NSMutableArray *) newOrderedArray:(NSMutableArray *)array ByKey:(NSString *)key ascending:(BOOL)ascending {
NSSortDescriptor *idDescriptor = [[NSSortDescriptor alloc] initWithKey:key ascending:ascending];
NSArray *sortDescriptors = [NSArray arrayWithObject:idDescriptor];
NSArray *orderArray = [array sortedArrayUsingDescriptors:sortDescriptors];
[idDescriptor release];
NSMutableArray *result = [NSMutableArray arrayWithArray:orderArray];
return result;
}
Is this a well-coded convenience method? As I think, it returns an autoreleased NSMutableArray.
This method is called by another one:
- (id) otherMethod {
NSMutableArray *otherResult = [[[NSMutableArray alloc] initWithCapacity:[otherArray count]] autorelease];
// I add some stuff to otherResult and then...
NSMutableArray *result = [dbUtils newOrderedArray:otherResult ByKey:#"objectId" ascending:NO];
return result;
}
This method (otherMethod) is called in some view controller where I want to store returned array and release it when deallocating the view controller. However, when [result retain] is called in this view controller (because I need it to be available and I can't allow it to be deallocated) I receive the following error:
[CFArray release]: message sent to deallocated instance
I've tried to log [result retainCount] just before calling retain and it print "1". I don't understand why an error is thrown when calling retain.
Thank you,
A
I don't see anything technically wrong with the code above--otherMethod should return an autoreleased NSMutableArray. Are you sure you're getting the error when calling retain? It looks more like you might be accidentally be sending release at some point instead of retain.
Stylistically, there's one thing--methods with "new" in the title should always return non-autoreleased objects, so you should either name your method something else (such as orderedArray...) or use [[NSMutableArray alloc] initWithArray:] instead of arrayWithArray. Also, method signatures shouldn't start with a capital (so ByKey should be byKey.
try this:
NSMutableArray *otherResult = [[NSMutableArray initWithCapacity:[otherArray count]];
Because initWithCapacity will return an autoreleased Array. Right now you tell the Autoreleasepool to release the Array twice.
initWithCapacity:does not return an autoreleased object. – Wevah
AFAIK initWithCapacity is a convenience initializier, which by convention return autoreleased objects. So if the object is only used within a local method, the autoreleasepool should deallocate it. Care to elaborate?