I'm still relatively new to iPhone development but thought I understood the basic principals of memory management. I have a class method that returns an NSMutableArray, I'm calling alloc and init on the object therefore know I'm responsible for releasing it. However because I'm returning the array I assumed I was supposed to use autorelease when creating the object instead of releasing it.
+(NSMutableArray *)getStations:(int)stationType {
if(database == nil){
[self openDataBase];
}
// Create a temporary array to hold the returned objects
NSMutableArray *holder = [[[NSMutableArray alloc] init] autorelease];
// Check if the statement has been defined
if(select4 == nil) {
const char *sql = "SELECT station_id, station_name, AVG(test_percent) FROM stations LEFT JOIN tests USING (station_id) WHERE station_type = ? GROUP BY station_id ORDER BY station_name ASC";
if(sqlite3_prepare_v2(database, sql, -1, &select4, NULL) != SQLITE_OK){
NSLog(#"Error while creating detail view statement. '%s'", sqlite3_errmsg(database));
}
}
sqlite3_bind_int(select4, 1, stationType);
// Check if the statement executed correctly
while(sqlite3_step(select4) == SQLITE_ROW) {
NSInteger primaryKey = sqlite3_column_int(select4, 0);
Tests *station = [[Tests alloc] initWithPrimaryKey:primaryKey];
station.station_name = [NSString stringWithUTF8String:(char *)sqlite3_column_text(select4, 1)];
station.average_score = [NSNumber numberWithDouble:sqlite3_column_double(select4, 2)];
[holder addObject:station];
[station release];
}
// Reset the detail statement.
sqlite3_reset(select4);
// Return the holder array
return holder;
}
There's the basic code - XCode no longer indicates a potential memory leak but it crashes everytime that code executes saying message sent to deallocated instance. Any help would be appreciated I've spent ages googling and can't see what's wrong with the code. I did find this thread but it doesn't appear to be the answer to my question - crash happens when NSMutableArray is returned?
The code you've posted appears to be managing memory correctly – you've got a one-to-one relationship between retains and (auto)releases, and you're making a textbook use of autorelease – so the problem is probably that the code calling this method needs to retain the resulting array before the autorelease pool kicks in and yanks the rug out from under you.
If your code is assigning the NSMutableArray to an ivar you've declared with #property, that ivar needs to be declared as
#property (retain) NSMutableArray *myStations;
If you're doing something else to store the array, you may need to call [myStations retain]. Your table view code will also need to release the array, probably in its dealloc method.
If you want to use the returned NSMutableArray as a data source to fill in rows in a table view, then you're going to need to retain it in your UITableView class (or your UITableViewDataSource delegate class). It's going to be called repeatedly whenever the view is scrolled or otherwise needs updating.
Easiest thing to do is make it a retained property in that class.
#property (nonatomic, retain) Tests * stationArray;
Then, say, if you want to get your data in your viewDidLoad method:
self.stationArray = [self getStations: self.stationID]; // property retains
Access it in numberOfRowsInSection:
return self.stationArray.count;
Access it in cellForRowAtIndexPath:
Tests *station = [self.stationArray objectAtIndex:indexPath.row];
And, of course, in dealloc...
[stationArray release];
The autorelease in the method is correct (not an init or copy method), but the class will need to retain it if it wants to use it later on, after the current event.
Related
I am developing an iphone application which has some data stored in a sqllite database. When my view loads i would like to load the data from the database on a background thread. The problem is the application keeps crashing and i dont know why.
The code:
-(id) init
{
if((self=[super init]))
{
[self performSelectorInBackground:#selector(loadList) withObject:nil];
}
}
-(void) loadList
{
#autoreleasepool
{
Loader * loader = [[Loader alloc] init];
NSMutableArray * array = [loader getItemList];
[array retain];
NSLog(#"Got %d items",[array count]);
[self performSelectorOnMainThread:#selector(createList:) withObject:array waitUntilDone:false];
[loader release];
}
}
-(void) createList: (NSMutableArray*) array
{
items = array;
int i;
Item * it;
for(i = 0; i < [items count]; i++)
{
it = [items objectAtIndex: i];
[it getName]; // crashes
// populate the list
}
}
Loader returns a NSMutableArray with Item objects. The application crashes when i call the item getName (which returns a NSString*). From what i understand it crashes because the item name properties is being released. What am i doing wrong?
Thanks!
It's likely to be a problem with whatever type of object you're using to populate array.
I'm unable to find finger-on-paper proof but I'm confident that performSelectorOnMainThread:withObject:waitUntilDone: retains its object. However if each of the items in array keeps a reference to loader then they need to take responsibility for retaining that object. It looks like you're attempting to keep it alive manually but — as Chuck alludes to — your call to performSelector... will return instantly and not wait for the call you've made to complete.
This particular bug appears to be that you're passing waitUntilDone:NO, so the array is being released immediately and consequently so are its items.
But in general, UIKit is not thread-safe, so this is just a touchy design. I would probably put the loading of this stuff in another class that handles the task for you instead of right in the view.
I'd put a breakpoint on the line:
it = [items objectAtIndex: i];
Then type
po it
in the debugger, and see what's in the name field. As a guess, I'd say one of two things: 1) the field that getName returns isn't initialized with an object (i.e. isn't a real NSString *) or that you're getting a C string from SQLite (which is what it usually returns) and you're trying to treat it as an NSString *. If it's the latter you can use [myCString stringWithUTF8String] to convert the C string into an NSString *
I have an instance variable called users defined as NSMutableArray.
I use that variable for fill an UITableView.
In viewDidLoad I initialize it with:
users = [[MySingleton sharedClass] getUsers];
This is the getUsers method:
- (NSMutableArray *)getUsers
{
...
NSMutableArray *listArray = [[NSMutableArray alloc] init];
for (NSDictionary *dict in jsonObject) {
...
[listArray addObject:element];
...
}
return listArray;
}
In this way all it works fine. The problem is when I set listArray as autoreleased object.
NSMutableArray *listArray = [[[NSMutableArray alloc] init] autorelease];
or
return [listArray autorelease];
Sometimes the app crash with EXC_BAD_ACCESS.
Why this? Isn't correct set autorelease listArray?
Assuming that users in users = [[MySingleton sharedClass] getUsers] is an instance variable, you're forgetting to take ownership of the array. When you want to claim ownership of an object (such as this array), you need to send it retain to tell it you want it to stick around. And when you're finished with it, you need to send it release to tell it so. Setters handle this for you, so it's generally a good idea to use setters outside of init and dealloc methods. So assuming you have a setter for users, you could do one of these:
self.users = [[MySingleton sharedClass] getUsers];
/* OR */
users = [[[MySingleton sharedClass] getUsers] retain];
The first way is usually better, but you don't want to call setters in init… or dealloc methods because they might have side effects that are undesirable there. Since you're not in one of those methods here, you can just use the first.
You have created and assigned an autoreleased object to user. By specifying autorelease you are saying that system could free it. So when it reaches the end of autorelease pool its removed from memory. That is why when you try to access it late it crashes. So if you need it to be global then you need to retain it.
I'm new to cocoa / objective-c and i'm struggeling with the releases of my objects. I have the following code:
gastroCategoryList = [[NSMutableArray alloc] init];
for (NSDictionary *gastrocategory in gastrocategories) {
NSString *oid = [gastrocategory objectForKey:#"id"];
GastroCategory *gc = [[GastroCategory alloc] initWithId:[oid intValue] name:[gastrocategory objectForKey:#"name"]];
[gastroCategoryList addObject:gc];
}
The analyzer shows me that the "gastrocategory" defined in the for is a potential memory leak. But i'm not sure if i can release this at the end of the for loop?
Also at the following code:
- (NSArray *)eventsForStage:(int)stageId {
NSMutableArray *result = [[NSMutableArray alloc] init];
for (Event *e in eventList) {
if ([e stageId] == stageId) {
[result addObject:e];
}
}
return result;
}
The Analyzer tells me that my "result" is a potential leak. But where should I release this?
Is there also a simple rule to memorize when i should use assign, copy, retain etc. at the #property ?
Another problem:
- (IBAction)showHungryView:(id)sender {
GastroCategoriesView *gastroCategoriesView = [[GastroCategoriesView alloc] initWithNibName:#"GastroCategoriesView" bundle:nil];
[gastroCategoriesView setDataManager:dataManager];
UIView *currentView = [self view];
UIView *window = [currentView superview];
UIView *gastroView = [gastroCategoriesView view];
[window addSubview:gastroView];
CGRect pageFrame = currentView.frame;
CGFloat pageWidth = pageFrame.size.width;
gastroView.frame = CGRectOffset(pageFrame,pageWidth,0);
[UIView beginAnimations:nil context:NULL];
currentView.frame = CGRectOffset(pageFrame,-pageWidth,0);
gastroView.frame = pageFrame;
[UIView commitAnimations];
//[gastroCategoriesView release];
}
I don't get it, the "gastroCategoriesView" is a potential leak. I tried to release it at the end or with autorelease but neither works fine. Everytime I call the method my app is terminating. Thank you very much again!
In your loop, release each gc after adding it to the list since you won't need it in your loop scope anymore:
gastroCategoryList = [[NSMutableArray alloc] init];
for (NSDictionary *gastrocategory in gastrocategories) {
NSString *oid = [gastrocategory objectForKey:#"id"];
GastroCategory *gc = [[GastroCategory alloc] initWithId:[oid intValue] name:[gastrocategory objectForKey:#"name"]];
[gastroCategoryList addObject:gc];
[gc release];
}
In your method, declare result to be autoreleased to absolve ownership of it from your method:
NSMutableArray *result = [[[NSMutableArray alloc] init] autorelease];
// An alternative to the above, produces an empty autoreleased array
NSMutableArray *result = [NSMutableArray array];
EDIT: in your third issue, you can't release your view controller because its view is being used by the window. Setting it to autorelease also causes the same fate, only delayed.
You'll have to retain your GastroCategoriesView controller somewhere, e.g. in an instance variable of your app delegate.
BoltClock's answer is spot-on as to the first part of your question. I'll try to tackle the rest.
Assign is for simple, non-object types such as int, double, or struct. It generates a setter that does a plain old assignment, as in "foo = newFoo". Copy & retain will, as their names imply, either make a copy of the new value ("foo = [newFoo copy]") or retain it ("foo = [newFoo retain]"). In both cases, the setter will release the old value as appropriate.
So the question is, when to copy and when to retain. The answer is... it depends. How does your class use the new value? Will your class break if some other code modifies the incoming object? Say, for example, you have an NSString* property imaginatively named "theString." Other code can assign an NSMutableString instance to theString - that's legal, because it's an NSString subclass. But that other code might also keep its own reference to the mutable string object, and change its value - is your code prepared to deal with that possibility? If not, it should make its own copy, which the other code can't change.
On the other hand, if your own code makes no assumptions about whether theString might have been changed, and works just as well whether or not it was, then you'd save memory by retaining the incoming object instead of unnecessarily making a copy of it.
Basically, the rule, which is unfortunately not so simple sometimes, is to think carefully about whether your own code needs its own private copy, or can correctly deal with a shared object whose value might be changed by other code.
The reason you can release gc after it is added to the gastroCategoryList is that when an object is added to an array, the array retains that object. So, even though you release your gc, it will still be around; retained by the gastroCategoryList.
When you are returning a newly created object from a method, you need to call autorelease. This will cause the object to be released only after the runtime leaves the scope of the calling method, thereby giving the calling method a chance to do something with the returned value.
Note that if your method starts with the word copy or new, then you should not autorelease your object; you should leave it for the calling method to release.
As for copy vs retain vs assign... as a general rule, copy objects that have a mutable version, such as NSArray, NSSet, NSDictionary, and NSString. This will ensure that the object you have a pointer to is not mutable when you don't want it to be.
Otherwise, use retain whenever you want your class to be ensured that an object is still in memory. This will apply to almost every object except for objects that are considered parents of your object, in which case you would use assign. (See the section on retain cycles here).
Also note that you have to use assign for non-object types such as int.
Read through the Memory Management Programming Guide a bit; it's quite helpful.
I have been very confused on how to handle the releasing of an NSMutableArray when I need to return it from a method. I am not even sure if I am suppose to release it or not.
I have the code:
-(NSArray *)methodToCall {
NSMutableArray *mutArray = [[NSMutableArray alloc] initWithCapacity:10];
//Fill the array with stuff here
[mutArray release]; //Am I suppose to have this to keep the memory down?
return mutArray;
}
My question is whether or not I am suppose to have the [mutArray release]; in the code or not. Could someone explain this? I am at a loss and I want to keep my code as clean and leak free as possible.
The caller of your method is going to expect a valid NSArray, but which it doesn't own.
If you release it the way you're doing in your question, you're returning an invalid object (because you've both allocated and released it.) So that's not what you want.
You should "autorelease" the array before returning it. Then you'll be allocating it, but relinquishing ownership (without forcibly releasing it) before returning it. It will be valid until the end of the current event loop when the autorelease pool gets cleaned up, and your caller can retain it or let it go as appropriate:
-(NSArray *)methodToCall {
NSMutableArray *mutArray = [[NSMutableArray alloc] initWithCapacity:10];
//Fill the array with stuff here
return [mutArray autorelease];
}
Your other alternative, since you don't need to keep the reference around either, is to use one of the "autoreleasing" convenience methods to create it in the first place:
-(NSArray *)methodToCall {
// We're not doing an explicit alloc/init here, so...
NSMutableArray *mutArray = [NSMutableArray arrayWithCapacity:10];
// ...no autorelease necessary.
return mutArray;
}
Short answer - No.
As it is now, you are allocating an array and then releasing (freeing) it before the return. So the when you try accessing the return object from your calling method you're going to get a crash.
My suggestion would be to use autorelease or to have the calling method or class ivar be responsible for this array object if it is used often.
An example of the autorelease:
NSMutableArray *mutArray = [[[NSMutableArray alloc] initWithCapacity:10] autorelease];
I also suggest reading the Memory Management from the Developer Documents.
if you go for explicit object allocation by calling alloc and init you are owner of your object, so you are responsible for its object retain value else you do it by implicit you don't need to care about it. it will take care of itself.
-(NSMutableArray *)getArray
{
NSMutableArray *array=[[NSMutableArray alloc] initWithCapacity:10];
//your code
return [array autorelease];
}
in the above code we are the owner of the object so we need to handle its retain count by passing autorelease the autoreleasepool will take care of it.
-(NSMutableArray *)getArray
{
NSMutableArray *array=[NSMutableArray allocWithCapacity:10];
//your code
return array;
}
in the above code we didn't alloc any object we just call class method to define the size of the array.
if you want more details go for the object ownership in Memory management guide from apple library
In general, instead of using alloc/init to create a temporary array, consider using a convenience creation method (+arrayWithCapacity: in this case):
- (NSArray *)methodToCall
{
NSMutableArray *mutableArray = [NSMutableArray arrayWithCapacity:10];
// Fill the array with stuff here
return mutableArray;
}
Convenience creation methods such as +arrayWithCapacity: return an object that the caller is not responsible for, relieving the calling code of the burden of managing memory. Since the calling code is simply returning the reference rather than storing it, that'll simplify things.
I'm doing an iPhone application which uses a navigation control to browse through some data. This data is stored in a sqlite database and I have a class which gets it and returns it in a NSMutableArray of NSStrings.
The problem is that in the first screen of the navigation everything works prefectly, but in the second screen (another view which is pushed) the same code fails because the NSMutableArray gets corrupted. In the debugger I can see that it is returned correctly, but when it's time to use it the pointers have become corrupted and the application crashes.
I have put breakpoints in all my functions, and I can't see anywhere where it can get corrupted. And as the first view, which uses the same exact code, even accesing the same eact tables, works correctly I don't really know where to look.
If anyone want to have a look at the code I have uploaded it to my site: http://sachafuentes.com/iBacus.zip
Thanks in advance.
UPDATE:
The problem lies in the function where I get the data, which looks like (this is a simplified version with some pseudo-code).
-(NSMutableArray *) getPaises {
NSMutableArray * paises;
paises = [[NSMutableArray alloc] init];
while( get new row ) {
NSString *aPais = get the value;
[paises addObject:aPais];
[aPais release];
}
return paises;
}
If I comment out [aPais release] everything works, but to me this looks like a memory leak, as the NSString won't be released.
Okay, here's the problem:
NSString *aPais = [NSString stringWithUTF8String:(char*)sqlite3_column_text(compiledStatement, 0)];
By convention, any time that you see an alloc and an init, you need a release somewhere down the road.
By convention, any time that you use an xWithX: style method, the resulting object will be released for you.
Therefore, this statement:
[aPais release];
will cause your method to crash, as the object is released before it should be. Just remove this line, set your NSMutableArray instance to be autorelease-d and you should get better results.
Look for wrong memory management, that's the likeliest cause for crashes. I think you release your objects somewhere where you shouldn't and therefore you have dangling pointers in the array.
Update1
[aPais release] is wrong, as you don't retain it anywhere in this method. Returned values should always be autoreleased. You should have the same amount of retain as release in your code.
(Some people argue that a retain can also be an alloc init, but I always try to use alloc init autorelease, but this is a matter of style.)
BTW
You should autorelease your array, you're only retaining it here with [[alloc] init].
Any object that you alloc and init must be release-d when you're finished with it, or you will have a memory leak.
Since you are passing the reference outside the scope of the getPaises method, you must autorelease the NSMutableArray, or you will have a memory leak:
paises = [[[NSMutableArray alloc] init] autorelease];
Can you please clarify the step here:
NSString *aPais = get the value;
It's not clear what happens in "get the value" and I suspect this is one cause of instability.
I see that the code is (verbatim)
while(sqlite3_step(compiledStatement) == SQLITE_ROW) {
NSString *aPais = [NSString stringWithUTF8String:
(char*)sqlite3_column_text(compiledStatement, 0)];
[paises addObject:aPais];
[aPais release];
}
...and it's exactly as #gs puts it. aPais is autoreleased and should not be released.
You can also use a convenience constructor to initialize the NSMutableArray:
NSMutableArray *paises = [NSMutableArray array];
This is equivalent to doing:
NSMutableArray *paises = [[[NSMutableArray alloc] init] autorelease];