ObjC threading and non-void functions memory management - iphone

Ok, so my question is something I have been looking for for a while. Say the method "first" has been detached as a new thread.
-(void)first{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
int a;
NSMutableArray *array = [self getArray];
[pool drain];
}
-(NSMutableArray *)getArray{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSMutableArray *ar = [NSMutableArray array];
[ar addObject:[NSString stringWithString:#"Hello"]];
return ar;
[pool drain];
}
My issue is that if i drain the pool after the object is returned, the pool doesnt get drained, and its leaked, however if i drain it before i return the array i cannot release the array because obviously its going to be needed...
This may be something thats really obvious, and i'm just missing but i am really confused. Thanks in advance.

It is unnecessary to have a second autorelease pool in the getArray method.
If, for some reason, you wanted to have an ARP in the getArray method, you'd probably implement it like this:
- (NSMutableArray *)getArray {
NSMutableArray *ar = [NSMutableArray array];
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
//do stuff
[pool drain];
return ar;
}
You technically can leave the pool un-drained, and it will get drained automatically when a "higher" pool gets drained, but IMO that's a sign of some really poorly designed code.

Related

How to use NSAutoreleasePool

If I use NSAutoreleasePool, every object created inside the pool should never be released mannually?It will be released when the pool is drained?
- (void) backgroundRequest{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
urlList = [[NSMutableArray alloc] init];
target = [[NSMutableArray alloc] init];
{
//do stuff in here
}
[urlList release];
[target release];
[pool release];
}
Are the above lines correct or I should remove [urlList release] and [target release]?
IMPORTANT: I will wait an explanantion and an answer.Thank you
An autorelease pool only works when you have objects using the Autorelease methods.
If you're using this:
urlList = [[NSMutableArray alloc] init];
Then you will want to do your own release, yes. However if you do this:
urlList = [[[NSMutableArray alloc] init] autorelease];
Then you can let the NSAutorelease pool handle that.
If you dont want to release urlList and target, then you can use this code:
- (void) backgroundRequest
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
urlList = [[[NSMutableArray alloc] init] autorelease];
target = [[[NSMutableArray alloc] init] autorelease];
{
//do stuff in here
}
[pool release];
}
Hope this helps you.
The point is that AutoreleasePool takes care of those allocations which have been autoreleased with the keyword autorelease So if you use that keyword then you don't need to release it. It is released when the AutoreleasePool gets released.
If you need more information then please leave me a message below.
I second the answer from #Brayden. I would like to add up to that.
Generally every thread has its autorelease pool. See your .main file for instance. It has got an autorelease pool associated with it. So that way your main thread has got an autorelease pool.
Now when you spawn another thread, ie try to run some methods on another thread, and if your gonna use class methods for common initialization.
eg: NSString *strTem = [NSString stringWithString:AnotherString];
Such is the case where autorelease will be used, and for such functions(methods) you need to have a separate autorelease pool of your own.
Hope this helps some more.

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];
}

Question about memory usage

I have the following method:
+(NSMutableDictionary *)getTime:(float)lat :(float)lon {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
[dictionary setObject:hour forKey:#"hour"];
[dictionary setObject:minute forKey:#"minute"];
[dictionary setObject:ampm forKey:#"ampm"];
return dictionary;
}
A lot of the method is chopped off, so I think I need the pool for some other stuff in the method. Here's my question. I know that I need to release the following objects:
[dictionary release];
[pool release];
However, I can't release the dictionary before I return it, but as soon as I return it the rest of the method isn't performed. What should I do?
You could always autorelease the dictionary, thereby ensuring it is kept in memory at least until getTime:: returns. This conforms well to the memory paradigm on Cocoa, where a method which returns an object which it creates (but does not own), calls autorelease on it when it no longer needs it.
Of course, make sure to retain that dictionary in any code that receives it from getTime::.

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.