newbie question on iOs memory management - iphone

How to write right in this situation:
I have some method, that return NSMutableArray*. Because method not started with init, new or alloc, how write in apple memory-management guide, i return autorealese object.
-(NSMutableArray*)someMethod {
NSMutableArray *array = [NSMutableArray alloc] init] autorealese];
//Some code here
return array;
}
And i have some another methods, that call this one:
-(NSMutableArray*)method1 {
NSMutableArray *array = nil;
if(condition){
array = [self someMethod];
}
return array;
}
-(NSMutableArray*)method2 {
NSMutableArray *array = nil;
array = [self method1];
}
Code work.But XCode analyze tool says that in method2 i get object with count 0. So, how to write this code good?

There is nothing wrong with your code, except that the method2 will return the array that is autoreleased. Thus whatever is calling this method should retain the return value.

Creating an autoreleased NSMutableArray and returning it.
-(NSMutableArray*)someMethod {
NSMutableArray *array = [NSMutableArray alloc] init] autorealese];
//Some code here
return array;
}
Method1 uses autorelease NSMutableArray from someMethod and for the life of Method1 the array will not be autoreleased. That's one of the rules of memory management in objective-c that object lives through the method cycle.
-(NSMutableArray*)method1 {
NSMutableArray *array = nil;
if(condition){
array = [self someMethod];
}
return array;
}
Method2 uses still waiting to be autoreleased NSMutableArray from method1. It's important to notice that b/c you have a condition in method2 the array might be nil.
-(NSMutableArray*)method2 {
NSMutableArray *array = nil;
array = [self method1];
}
So in another words, you are passing autoreleased object along your methods. There is nothing wrong with this. You just have to remember that if you want to store the value of method2 you need to retain it, or it will get autoreleased.
Because of your condition in method1 the analyzer will complain b/c it's not guaranteed that the method1 will return an object, there is a possibility it will return nil.

Related

NSMutableArray: Memory management while calling methods

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

Leak in NSMutableArray

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).

iPhone - Problems with releasing memory - memory leaks

I'm having the code as below.
- (void)viewDidLoad
{
NSArray* myarr = [self createArray];
for (NSString* str in myarr)
{
NSLog(#"%#",str);
}
[myarr release];
}
-(NSArray*)createArray
{
NSArray* arr1 = [[NSArray alloc] initWithObjects:#"APPLE",#"MAC",#"IPHONE",nil];
return arr1;
}
When I "Build & Analyze", its showing two leaks. One at [myarr release] saying, incorrect decrement of the reference count of an object that is owned at this point. and Other at return arr1, saying, Potential leak of an object allocated on line 152 and stored into arr1.
From my above code, the method "createArray" is returning a pointer and I'm releasing it as well. Is my way of coding right or wrong?
From my above code, the method "createArray" is returning a pointer and I'm releasing it as well. Is my way of coding right or wrong?
that depends on how you look at it.
1) the ref counting looks ok
2) the static analyzer flags objc methods based on names, in some cases. so the issue will likely vanish if you rename createArray to newArray, or something named new*. so it expects a convention (the ones used by Apple) to be followed.
therefore, it's the message that's bit shallow, it doesn't really analyze the program, but bases its findings/results on convention -- and not an actual evident issue which a human can read.
If you're just using the array in your viewDidLoad method, then you don't need to alloc an array in there at all. You can just use an autoreleased array returned as 7KV7 suggested. You can return an autoreleased array in your -(void)createArray as well without alloc'ing an object. Here is an example.
- (void)viewDidLoad
{
NSArray* myarr = [self createArray];
for (NSString* str in myarr)
{
NSLog(#"%#",str);
}
}
-(NSArray*)createArray
{
return [NSArray arrayWithObjects:#"APPLE",#"MAC",#"IPHONE",nil];
}
If you don't have to alloc an object to use it, it makes for less, and cleaner code, IMO.
Try this
- (void)viewDidLoad
{
NSArray* myarr = [[NSArray alloc] initWithArray:[self createArray]];
for (NSString* str in myarr)
{
NSLog(#"%#",str);
}
[myarr release];
}
-(NSArray*)createArray
{
NSArray* arr1 = [[NSArray alloc] initWithObjects:#"APPLE",#"MAC",#"IPHONE",nil];
return [arr1 auotrelease];
}
The problem with your code is that
You do not allocate myarr using alloc or new so you do not take ownership of the object. Hence the issue in release.
You allocate arr1 so you take ownership of the object and you return arr1. Hence you do not release it. That is the reason for the leak.

Can't Figure Out How To Fix Memory Leaks on iPhone

I was running Leaks tool and discovered a massive leak in my Dictionary mutableDeepCopy but I can't figure out what's wrong with the code. Any suggestions?
#interface RootViewController : UIViewController{
NSDictionary *immutableDictionary;
NSMutableDictionary *mutableDictionary;
}
Here is the line of code that's highlighted in Instruments
self.mutableDictionary = [self.immutableDictionary mutableDeepCopy];
Here is the method for creating a mutable copy of a Dictionary
#interface NSDictionary(MutableDeepCopy)
-(NSMutableDictionary *)mutableDeepCopy;
#end
Here is method implementation, I've highlighted the code that Leaks saids is leaking 100%
- (NSMutableDictionary *) mutableDeepCopy {
NSMutableDictionary *dictionaryToReturn = [NSMutableDictionary dictionaryWithCapacity:[self count]];
NSArray *keys = [self allKeys];
for(id key in keys) {
id value = [self valueForKey:key];
id copy = nil;
if ([value respondsToSelector:#selector(mutableDeepCopy)]) {
copy = [value mutableDeepCopy];
} else if ([value respondsToSelector:#selector(mutableCopy)]) {
copy = [value mutableCopy]; //This is the Leak
}
if (copy == nil) {
copy = [value copy];
}
[dictionaryToReturn setValue:copy forKey:key];
}
return dictionaryToReturn;
}
You need to analyse this in light of Apple's Memory Management Rules.
Starting with this line:
self.mutableDictionary = [self.immutableDictionary mutableDeepCopy];
I would expect mutableDeepCopy to return an object I own, so at some point I need to release or autorelease it. e.g.
NSMutableDeepCopy* temp = [self.immutableDictionary mutableDeepCopy];
self.mutableDictionary = temp;
[temp release];
or
self.mutableDictionary = [[self.immutableDictionary mutableDeepCopy] autorelease];
So now we need to look at mutableDeepCopy. Because it has 'copy' in the name it needs to returned an "owned" object which, in practice means "forgetting" to release the returned object. You have already failed to do that when you create the returned object in the first line, since dictionaryWithCapacity: gives you an object you do not own. Replace it with
NSMutableDictionary *dictionaryToReturn = [[NSMutableDictionary alloc] initWithCapacity:[self count]];
Now you own it.
It is important that you make your mutableDeepCopy obey the rules because it means you can treat the objects returned from mutableDeepCopy, mutableCopy and copy in exactly the same way. In all three cases you own the object copy that you insert into the array. Because you own it, you must release it or it'll leak as you found out. So, at the end of the loop, you need
[copy release];
That'll stop the leak.
How is your property declared? If is is retain or copy, then this doesn't leak.
Your problem is that the name mutableDeepCopy suggests that it returns a retained object, and not an autoreleased one as it actually does.
Edit:
And at the mutableDeepCopy itself, you need to release the copy variable after adding to the dictionary.
mutableCopy increments the retain count of the object, as does setValue:forKey:. This means that when dictionaryToReturn is dealloc'ed, the object that had mutableCopy called still has a retain count of one.
Try doing this instead:
copy = [[value mutableCopy] autorelease];

[CFArray release]: message sent to deallocated instance

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?