Memory Management,Adding objects to array - iphone

Hi friends I am using this code
-(void)searchItem:(NSMutableArray*)items
{
if ([[[items objectAtIndex:1]objectForKey:#"Success"]isEqualToString:#"True"]) {
NSMutableArray *searcharr=[[NSMutableArray alloc]init];
for (int i=2; i<[items count]; i++)
{
NSAutoreleasePool *pool=[[NSAutoreleasePool alloc]init];
[searcharr addObject:[items objectAtIndex:i]];
NSLog(#"data fetch array is====> /n%#",searcharr);
[pool release];
}
searchItemsArray=[[NSMutableArray alloc]initWithArray:searcharr];
[searcharr release];
searcharr=nil;
}
I have released searchItemsArray in dealloc method.The items array i am getting from a webservice.It contains images and other data.I was using the for loop without NSAutoreleasePool,But when i used the instrument with simulator,i was getting the leak here.I just want to know that the code which i have given here with pool is correct or not.My app was also crashing as i was feeding this data and images in tableview cell.So please help me out.
And one more thing should i always use NSAutorelease pool,while looping ......Thanks

if the method "-(void)searchItem:(NSMutableArray*)items" get's called many times then searchItemsArray will be allocated each time resulting a memory leak. If you call release in the dealloc that means you will release only the instance created at the last call of the method.
you could do something similar to this:
-(void)searchItem:(NSMutableArray*)items
{
if ([[[items objectAtIndex:1]objectForKey:#"Success"]isEqualToString:#"True"])
{
if( nil == searchItemsArray )
searchItemsArray = [[NSMutableArray alloc]init];
else
[searchItemsArray removeAllObjects];
for (int i=2; i<[items count]; i++)
{
[searchItemsArray addObject:[items objectAtIndex:i]];
}
}
If you created the items array with alloc/init then after the call of "-(void)searchItem:(NSMutableArray*)items" you need to release it also.
If in a loop you create many many autoreleased objects that you will not use later then the autorelease pool would be an option.
Every time -autorelease is sent to an object, it is added to the inner-most autorelease pool. When the pool is drained, it simply sends -release to all the objects in the pool.
Autorelease pools are simply a convenience that allows you to defer sending -release until "later". That "later" can happen in several places, but the most common in Cocoa GUI apps is at the end of the current run loop cycle.
NSAutoreleasePool *pool;
pool = [[NSAutoreleasePool alloc] init];
for(int i = 0; i < 100; i++)
{
NSString *string = [[[NSString alloc] init] autorelease];
/* use the string */
}
[pool drain]; //or release if you don't plan to use the pool later.
The code wasn't compiled sorry for the possible errors.
You can find more info about autorelease pools at this link

Related

iphone: How to solve NSArray memory Leak?

I am releasing NSArray and NSMutableArray but its show memory leak. while ZoneData code is like this
-(ZoneData*) initWithZoneName:(NSString *)zoneNameIn SdName:(NSString *)sdNameIn eCount:(NSString *)eCountIn iCount:(NSString *)iCountIn StandLat:(NSString *)standLatIn StandLong:(NSString *)standLongIn
{
self = [super init];
if (self)
{
zoneName = [zoneNameIn copy];
lsdName = [sdNameIn copy];
leCount = [eCountIn intValue];
liCount = [iCountIn intValue];
standLat = [standLatIn copy];
standLong = [standLongIn copy];
}
return self;
}
how to solve this?
The problem is your instance variables. In your -init, you are correctly assigning them to copies of the strings from the array. However, you need t also release them in -dealloc.
-(void) dealloc
{
[zoneName release];
[lsdName release];
[standLat release];
[standLong release];
[super dealloc];
}
Now, you may be asking why the leaks tool is telling you the leaks are where you create the NSArray with the strings in it instead of the init method. The reason is that -copy for immutable objects is optimised to do nothing except send retain to self. So those copies you have as instance variables are in reality the same objects as was created by -componentsSeparatedByString:
componentsSeparatedByString: returns an autoreleased NSArray. You are not supposed to release that yourself, but the closest NSAutoreleasePool will do that for you. In line 61 you are overreleasing the array.
If you are concerned about the memory usage while performing the loop you can clear autoreleased objects in each iteration of the loop:
for (...)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// your loop contents.
[pool drain];
}

What is the Ideal way to return data from function without leaking memory?

In IPhone Development ObjC , I wonder what is the right approach for functions to return some values in Dictionaries or Arrays. I mean should they return autorelease objects, but wouldn't it be bad if I am using too many functions to return values, like getUserRecordsFromDB() function will return all the user records, so should they return me autorelease objects? also when I am calling them multiple times, suppose with a span of 4 seconds to get the newely updated data. Can somebody let me an ideal scanario of how to get data from a function called frequently in the flow of program, and not leaking the memory.
We should always return autorelease objects from functions and ensure they get released by setting up Autorelease pool in the calling function as follows
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// Call database function here...
[pool release];
Refer the auto release pool documentation here
If the method is in same class use global variable, if it is in some other class make sure it is allocated only one time, reuse the same memory every time it call that method.
Also check NSAutoreleasePool
Yes you should use autoreleased objects as return values.
You should stick to the cocoa programming guidelines.
This is a place to start, but there is a lot more to find on the subject.
You can always pass a mutable object to be filled in.
-(void) getMoreUsers:(NSMutableArray *)ioArray {
for ( ... ) {
NSMutableDictionary *user = [NSMutableDictionary new];
// fill in user ...
[ioArray addObject:user];
[user release]; // explicit release so only reference is owned by ioArray
}
}
This gives you explicit control when you know the lifetime of an object, but makes it easy to write a wrapper when you want to use autorelease.
-(NSArray *) getUsers {
NSMutableArray *result = [NSMutableArray array]; // implicit autorelease
[self getMoreUsers:result];
return result;
}
An example of using this with a known lifetime would be:
-(void) operateOnUsers {
NSMutableArray *array = [NSMutableArray new];
[self getMoreUsers:array];
// do stuff that will not retain array
[array release];
}

iPhone Autoreleasepool and allocations

I've been reading about autoreleasepool but there is a point which is a bit unclear to me. I have some functionality using threads that required seperate memory managment using autoreleasepool.
In the following example is correct
-(void) doSomething {
NSAutorelease *pool = [[NSAutorelasepool alloc] init];
NSString *myString = #"Hello";
[pool release];
}
Is this correct?
-(void) doSomething {
NSAutorelease *pool = [[NSAutorelasepool alloc] init];
NSString *myString = [[NSString alloc] initWithString:#"Hello"];
[pool release];
}
or this?
-(void) doSomething {
NSAutorelease *pool = [[NSAutorelasepool alloc] init];
NSString *myString = [[NSString alloc] initWithString:#"Hello"];
[myString release];
[pool release];
}
My question is owned objects created in the scope of the autorelease pool need to be relased specifically or are the taken care of when the autorelasepool is been released?
Teo
Autorelease pool handles the autoreleased objects. If you own an object (via alloc or copy or retain) then you must release it. So your 2nd example is not correct. As you have allocated the string, you own it and you must release it.
An autorelease pool is created for the main thread. (You can look into the main function if you want). Every thread need its own autorelease pool to manage autoreleased objects. That's why if you create another thread then you must create an autorelease pool for that thread. Even if you don't create autoreleased object in the thread, you should create this as the library calls in that thread may create autoreleased objects. Even if you are sure that no library calls are making autoreleased objects then you also should create them as that is the best practice, specially if you are working on big project which is developed and maintained by multiple people.
You only need to create your own autorelease pool when you are creating a bunch of
autoreleased objects you want to garbage collect immediately. However, you are correct in that you don't want to reference any "autoreleased" objects you create after you release the pool. Autoreleased objects (which you don't retain) are destroyed when the pool is drained.
Since none of the objects in your example are autoreleased, creating your own autorelease pool is essentially a no-op.
Neither of your examples needs an autorelease pool. Autorelease pools only take care of autoreleased objects:
NSArray *foo = [NSArray array];
NSObject *bar = [[[NSObject alloc] init] autorelease];
Your first string is initialized using a string literal and therefore is probably special with respect to memory management (maybe someone else knows more). Your second string leaks, the pool does not make a difference. Your third string is released correctly, again the pool does not make a difference.
This is where you would need a pool:
- (void) someMethodThatRunsOnAThread {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *foo = [#"foo" uppercaseString];
[pool drain];
}
Here the foo string would leak if the pool wasn’t there. Note that I’m calling drain instead of release on the pool – on iOS there’s not a difference, but in garbage-collected environments the two differ, so it’s probably better to get in the habit of calling the right one.
Also note that you may need a pool even though you don’t autorelease any objects yourself, there could be many memory operations done somewhere in the code you’re calling in your method.
Think that this should be something like this:
-(void) doSomething {
NSAutorelease *pool = [[NSAutorelasepool alloc] init];
NSString *myString = [[[NSString alloc] initWithString:#"Hello"] autorelease];
// or create string like this (automatically autoreleased)
NSString *myString = [NSString stringWithString:#"Hello"];
[pool release];
}
You must send autorelease message, to objects inside autorelease pool. They will be released when release message is sent to pool.

Data Formatters temporarily unavailable, will re-try after a 'continue'

Here is the error message I get:
ContactsWithPN - start loop
Program received signal: “0”.
Data Formatters temporarily unavailable, will re-try after a 'continue'. (Unknown error loading shared library "/Developer/usr/lib/libXcodeDebuggerSupport.dylib")
Here is the code that causes this problem:
+(NSArray *) contactsWithPhoneNumbers{
NSArray *contacts = [ABContactsHelper contacts];
NSMutableArray *rv = [[NSMutableArray alloc] init];
NSLog(#"ContactsWithPN - start loop");
for (int i = 0; i< [contacts count] ; i++) {
ABContact * c = (ABContact*)[contacts objectAtIndex:i];
ABContact * fullContact = [ABContact contactWithRecordID:[c recordID]];
if ([[fullContact phoneArray] count] > 0) {
[rv addObject:fullContact];
}
}
NSLog(#"ContactsWithPN - end loop");
NSArray *ret = [[NSArray alloc] initWithArray:rv];
return ret;
}
In the View Controller that calls the said class method, I added the following code to see if memory warnings were being sent. They are not!
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
NSLog(#"InviteFriends - memory warning received");
}
Observations:
+ Found that error occurs at different points in time - sometimes at index 253, other times at 246..
+ Only happens on the IPhone - not the simulator (on the simulator, there are < 5 contacts)s
The fact that you haven't received the memory warning doesn't mean that it wasn't sent: Memory warnings are delivered to the main run loop; they will not be delivered while your function is still running.
Instead, consider looking at the phone console (Xcode->Organizer->Your phone->Console, or the equivalent in iPCU). If it says something like "memory level is critical" and mentions killing your app, then you've run out of memory. Additionally, when you run out of memory, the crash reporter writes a "low memory" crash log with "jettisoned" beside the processes that were killed; you should see these in the Organizer. (Since iOS 4's "multitasking", jettisoning also happens to background tasks.)
If it's due solely to the large pile of autoreleased objects, you can mitigate it to some extent with explicit autorelease pools:
for (int i = 0; i< [contacts count] ; i++) {
NSAutoreleasePool * pool = [NSAutoreleasePool new];
...
[pool drain]; pool = nil;
}
Your code also leaks ret and rv.
This error occurs when your app is running out of memory. You should read Apples Memory Management Guide.
First thing is that you have memory leaks in your code... Fix that like this
+(NSArray *) contactsWithPhoneNumbers{
NSArray *contacts = [ABContactsHelper contacts];
NSMutableArray *rv = [[NSMutableArray alloc] init];
NSLog(#"ContactsWithPN - start loop");
for (int i = 0; i< [contacts count] ; i++) {
ABContact * c = (ABContact*)[contacts objectAtIndex:i];
ABContact * fullContact = [ABContact contactWithRecordID:[c recordID]];
if ([[fullContact phoneArray] count] > 0) {
[rv addObject:fullContact];
}
}
NSLog(#"ContactsWithPN - end loop");
NSArray *ret = [[NSArray alloc] initWithArray:rv];
//You need to release rv since you dont need it any more as you have copied the contents to a new array ( this itself is a waste ) so you must release the old array
[rv release];
//Now when you return the array you must still not hold object ownership since the function dies after returning so you give it a delayed release which kicks in when the autorelease is flushed.
return [ret autorelease];
}
Normally autorelease is flushed at certain times decided by the OS however you can create your own pool to make sure that you don't waste resources. So Ideally you will make the call like this
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSArray *contactArray = [[self contactsWithPhoneNumbers] retain];
[pool drain];
//This will release the object ownership held by the function
Finally so you do all this and you don't have memory leaks but you still get this error. The answer because the memory warning didn't get to you just as #tc. has said. So the simple answer is that the main run loop was clogged. What you can do is maybe do this operation in a separate thread to make sure that the main loop is not clogged... If you are on iOS 4+ you can do this easily by
dispatch_queue_t otherQueue = dispatch_queue_create("com.company.otherqueue", NULL);
dispatch_async(otherQueue, ^{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSArray *contactArray = [[self contactsWithPhoneNumbers] retain];
[pool drain];
}
dispatch_release(otherQueue);
Now this necessarily does not mean that it will create a new thread however the os will manage the queue such that the main queue does not get blocked and you will receive the memory warning. From then on you must release the memory and make sure you dont go over.
I discover ABContact also leaks memory,see part of ABContactHelper code below.
+ (NSArray *) contacts
{
ABAddressBookRef addressBook = ABAddressBookCreate();
NSArray *thePeople = (NSArray *)ABAddressBookCopyArrayOfAllPeople(addressBook);
NSMutableArray *array = [NSMutableArray arrayWithCapacity:thePeople.count];
for (id person in thePeople)
{
[array addObject:[ABContact contactWithRecord:(ABRecordRef)person]];
}
[thePeople release];
//I think need CFRelease(addressBook); here
return array;
}

How do I create a local autorelease pool to save up memory?

Apple says that this is a good idea for saving memory. What would that look like in code?
Usualy you don't need to create autorelease pool, because system cares about this. But, sometimes you need to do this. It's usualy in big loops. Code would look like this:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
int i;
for (i = 0; i < 1000000; i++) {
id object = [someArray objectAtIndex:i];
// do something with object
if (i % 1000 == 0) {
[pool release];
pool = [[NSAutoreleasePool alloc] init];
}
}
[pool release];
Autorelease pools are kept as a stack: if you make a new autorelease pool, it gets added to the top of the stack, and every autorelease message puts the receiver into the topmost pool.