I am stuck in the middle of memory management stuff. Please help me out in solving my question.
NSMutableArray *array = [[NSMutableArray alloc]init];
Object *obj = [[Object alloc]init];
[array addObject: obj];
[obj release];
Is it necessary to release obj in above code?
The answer to your question is: yes, if you don't use ARC. If you are writing a new app, you should seriously consider using ARC.
Container objects in Objective-C always balance their retain/release count. In other words, you should always manage memory as if you did not add the object and make sure your own code balances its retain count. Note that this is a convention and is not enforced, but you could always trust the built-in classes to follow this convention. Also, you can perform a static analysis (Cmd+Shift+B in XCode) to detect these problems. It would have pinpointed this in your code above.
The correct code in the case above would be:
NSMutableArray *array = [[NSMutableArray alloc]init];
Object *obj = [[[Object alloc]init]autorelease];
[array addObject: obj];
or
NSMutableArray *array = [[NSMutableArray alloc]init];
Object *obj = [[Object alloc]init];
[array addObject: obj];
[obj release];
since NSMutableArray (and its cousins) will retain the object as long as it is in the collection.
Related
I have a piece of code & I want to understand the memory leak possibility in this. I have gone through the Apple documentation here!
The code here process a array from parameter & stores the dictionary into a temporary array.
+ (void)setLinkedProfiles:(NSArray *)profileData {
NSMutableArray *returnArray = [[NSMutableArray alloc]init];
if([profileData count] > 0) {
for(NSDictionary *dict in profileData) {
NSDictionary *tempDict = #{
#"verifiedEmail" : ([[dict objectForKey:#"verifiedEmail"] isKindOfClass:[NSNull class]]) ? #"": [dict objectForKey:#"verifiedEmail"],
#"identifier" : [dict objectForKey:#"identifier"],
};
[returnArray addObject:tempDict];
}
}
[SharedApp sharedUserData].linkedProfiles = returnArray;
}
I suspect that the line of code SharedApp sharedUserData].linkedProfiles = returnArray might create a memory leak.
I want to understand why & in which scenario?
Can some one help me in understanding this ?
Thank you for reading & understanding my problem.
To clear things up:
Your line
NSMutableArray *returnArray = [[NSMutableArray alloc]init];
gives you ownership of that array. You are responsible to release it when you're done with it.
So adding
[returnArray release];
as the last line would work. You could use autorelease instead, or even work with an autoreleased array from the beginning, i.e.
NSMutableArray *returnArray = [NSMutableArray array];
linkedProfiles should be a strong reference (i.e. strong, or copy).
Although I strongly suggest to switch to ARC, understanding of the underlying memory management might come handy.
I know that when we add an object obj into a NSMutableArray, it takes the co_ownership of that object. So we can release the object obj just after adding that into the NSMutableArray. And when we release the NSMutableArray, it also calls the release of all the objects in it. So there is no memory leak and every thing is fine.
My doubt is if do like
NSMutableArray myArray = [[NMutableArray alloc] init];
[myArray addObject:[MyClass alloc] init]];
[myArray release];
Will this Cause any memory leak in our program?
Yes indeed, it will cause a memory leak.
The offensing line is (I have splitted it for clarity):
[myArray addObject:
[[MyClass alloc] init] // <- An instance is allocated with ownership
]; // <- The array retains the instance
At the end, the retain/release ownership are not balanced and causes the leak.
One solution is to make the allocation before the addition:
MyClass *obj = [MyClass alloc] init];
[[myArray addObject:obj];
[obj release];
Put autorelease for your MyClass. That should fix any memory leaks
NSMutableArray myArray = [[NMutableArray alloc] init];
[myArray addObject:[[[MyClass alloc] init] autorelease];
[myArray release];
I have a memory leak when i call a method that return me a string----
the method definition is as follows
-(NSMutableArray *)read
{
NSMutableArray *dataArray = [[NSMutableArray alloc] init];
//picking data from database here
return dataArray;
}
this show a big memory leak
i also tried--- NSMutableArray *dataArray = [[[NSMutableArray alloc] init]autorelease];
but this time leack checking process gets hanged
i also cannot release that array before return
please help
-(NSMutableArray *)read
{
NSMutableArray *dataArray = [[NSMutableArray alloc] init];
//picking data from database here
return dataArray;
}
Anything that uses the method read will expect to get back an object it does not own. However, as written here, dataArray is still owned at the point of return. You can't release it because that might make it go away altogether. You must, in this instance autorelease the array. You can either do this:
-(NSMutableArray *)read
{
NSMutableArray *dataArray = [[[NSMutableArray alloc] init] autorelease];
//picking data from database here
return dataArray;
}
or this
-(NSMutableArray *)read
{
NSMutableArray *dataArray = [[NSMutableArray alloc] init];
//picking data from database here
return [dataArray autorelease];
}
You say "leak checking process get hanged" but I'm really not sure what you mean by that. Whether it hangs, crashes or plays the Botswana National Anthem, you definitely need to autorelease the returned array and any other problem is actually a different problem. Possibly, you are forgetting to retain the data elsewhere.
Another answer more...
There are many conventions in cocoa/cocoa-touch, there is one of them that says that if a method has the prefix init then you will have the ownership of that object (hence you have to release it)
This is NOT your case, hence if you do:
DatabaseReader *dbReader = [[DatabaseReader alloc] init];
NSMutableArray *mutArray = [dbReader read];
[dbReader release];
you are NOT supposed to release mutArray. BUT, the object created HAS to be released by someone. So you can do as JeremyP wrote. alloc/init and put it into a autorelease pool inside read method implementation. Or, you can do:
-(NSMutableArray *)read
{
NSMutableArray *dataArray = [NSMutableArray array];
//IMPORTANT:
//Did you noticed that I am not using any method
//with init prefix for the creation of dataArray ?
//so I don't need to release by my self ;)
//picking data from database here
return dataArray;
}
Which is basically the same. ;)
Ownership of the returned object may be returned to the object that receives from this function. You may do some debugging with the object's retain count using something like this...
NSLog(#"Retain count: %i", [dataArray retainCount]);
Turn on the debugging console (Command + R in Xcode) to see the NSLog output.
I started dealing with NSOperations and (as usual with concurrency) I'm observing strange behaviour.
In my class I've got an instance variable:
NSMutableArray *postResultsArray;
when one button in the UI is pressed I initialize the array:
postResultsArray = [NSMutableArray array];
and setup the operations (together with dependencies).
In the operations I create a custom object and try to add to the array:
PostResult *result = [[PostResult alloc] initWithServiceName:#"Sth" andResult:someResult];
[self.postResultsArray addObject:result];
and while adding I get:
-[CFArray retain]: message sent to deallocated instance 0x3b40c30
which is strange as I don't release the array anywhere in my code (I did, but when the problem started to appear I commented all the release operations to be sure that they are not the case). I also used to have #synchronized section like below:
PostResult *result = [[PostResult alloc] initWithServiceName:#"Sth" andResult:someResult];
#synchronized (self.postResultsArray) {
[self.postResultsArray addObject:result];
}
but the problem was the same (however, the error was for the synchronized operation).
Any ideas what I may be doing wrong?
postResultsArray = [NSMutableArray array];
[NSMutableArray array] is a convient method which is equivalent to [[[NSMutableArray alloc] init] autorelease]. So there is an implicit (auto)release there. Since you're going to keep it, you have to use one of these 3 changes:
postResultsArray = [[NSMutableArray array] retain];
postResultsArray = [[NSMutableArray alloc] init];
Exploit the fact that a setter should retain the new value (if you declare as #property(retain)):
self.postResultsArray = [NSMutableArray array];
I have a list of shops in a ListController file.
I've setted up a sqlite db, in which i've stored 60 shops.
On the top of the list i have a search bar.
I've made a class called DataController, that is responsible to load and store db datas.
#interface DataController : NSObject {
sqlite3 *database;
NSArray *shops;
NSDictionary* dictionaryOfShops;
}
#property (nonatomic, retain) NSDictionary *dictionaryOfShops;
#property (nonatomic, retain) NSArray* shops;
-(void)initializeShops;
initializeShops method loads data from the db, and stores results into the 2 props in this way:
-(void)initializeShops{
[dictionaryOfShops release];
[shops release];
NSMutableDictionary *dictionary = [[[NSMutableDictionary alloc] init] autorelease];
if (sqlite3_open(....))
NSString *query = ....
if (sqlite3_prepare_v2(database, [query UTF8String],-1, &statement, nil) == SQLITE_OK)
{
while (sqlite3_step(statement) == SQLITE_ROW) {
int rId = sqlite3_column_int(statement, 0);
char *rName = (char *)sqlite3_column_text(statement, 1);
Shop* s = [[Shop alloc] init];
s.ID = rId;
if(sName != nil) s.Name = [NSString stringWithUTF8String:rName];
NSString *shopID = [[NSString alloc] initWithFormat:#"%d",s.ID];
[dictionary setObject:s forKey:shopID];
[shopID release];
[s release];
}
sqlite3_finalize(statement);
}
[query release];
dictionaryOfShops = [[NSDictionary alloc] initWithDictionary:dictionary];
shops = [[NSArray alloc] initWithArray:[dictionary allValues]];
dictionary = nil;
[dictionary release];
//Sorting
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:#"Name" ascending:YES];
NSArray *sortedList =[self.shops sortedArrayUsingDescriptors:[NSArray arrayWithObject:sort]];
self.shops = sortedList;
[sort release];
}
The problem is that when user enters some text into the search
bar, I change the value of the query (adding LIKE....) and then call the initializeShops method again. This second time makes
so many leaks, (related to the Shop class properties) and
leaks also a NSDictionary and a NSArray.
Before posting this to you I've tried different solutions, but
at least this doesn't leaks anything the first time I call
initilizeShops.
I accept any suggestion, since I'm really stuck
on it.
MORE:
The really strange thing is memory management of my var dictionary and the 2 props shops and dictionaryOfShops. With this code
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
//add data to dictionary
dictionaryOfShops = [[NSDictionary alloc] initWithDictionary:dictionary];
shops = [[NSArray alloc] initWithArray:[dictionary allValues]];
[dictionary release]
Considering that dictionaryOfShops and shops are two properties (nonatomic,retain) synthesized, how can I change value to them without leaks?
The very first time I pass through this method nothing gets leaked, from the second time it starts to leak so many objects (the contents of the collections).
The first question is Why not just use Core Data? It is very likely going to be faster, will require less code, and will be significantly easier to maintain over time. To be blunt; SQLite is deceptively hard. Easy to get started, exceptionally difficult to get right.
In any case, the memory management of dictionary is wrong. It only isn't crashing because you swapped the order of the nil assignment and release as kennyTM suggested. I would suggest not creating an autoreleased dictionary.
Otherwise, the code as written seems pretty leakless at first glance. So:
Can you provide some more code?
Anything interesting memory wise
going on elsewhere?
Are you using threading at all (or
NSOperationQueue)?
Have you run under the Leaks
instrument and retrieved the
backtraces of allocation of the
specific objects being leaked?
dictionary = nil;
[dictionary release];
Please swap these 2 statements. In this form it means [nil release] which is a no-op.
Ok, I've found the error.
In my class Shop, i realize i didn't implement the method
-(void)dealloc
So when I release the old dictionary (to prepare for a new assignment), all the fields inside of it didn't get released.