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;
}
Related
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.
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
I am changing the image in UIImageView based on accelerometer input. The images are stored in an array.
The application runs fine for a while and then crashes.
warning: check_safe_call: could not restore current frame
I am not using "UIImage ImageNamed" method when populating the array.
The total size of all images is around 12 Mb. But individual images are very light (<100 kb) and i am using only one image at a time from the array.
Just in case there are any autoreleases, I have allocated a NSAutoreleasePool in view did load and am draining it in the didReceiveMemoryWarning method (which does get called 2, 3 times before the app crashes?).
Following is the code that creates images array:
//create autorelease pool as we are creating many autoreleased objects
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSMutableArray *finalarr = [[NSMutableArray alloc] initWithCapacity:9];
NSLog(#"start loading");
for(int y = 0; y < 100; y+=10)
{
NSMutableArray *arr = [[NSMutableArray alloc] initWithCapacity:10];
for(int x = 0; x < 10; x++)
{
NSString *imageName = [[NSString alloc] initWithFormat:#"0%d",y + x];
UIImage *img = [[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:imageName ofType:#"png"]];
[arr addObject:img];
[imageName release];
[img release];
}
[finalarr addObject:arr];
[arr release];
}
NSLog(#"done loading");
// Override point for customization after app launch
viewController.imagesArray = finalarr;
[finalarr release];
//retain the array of images
[viewController.imagesArray retain];
//release the aurtorelease pool to free memory taken up by objects enqued for release
[pool release];
As the app runs smoothly for a while, which means array creation is definitely not the problem.
After this the following method is called from [accelerometer didAccelerate]
-(BOOL)setImageForX:(int)x andY:(int)y
{
[self.imageView setImage:(UIImage*)[(NSArray*)[self.imagesArray objectAtIndex:y] objectAtIndex:x]];
return TRUE;
}
So the only code being executed is the "UIImageView setImage". But no objects are being created here.
Please let me know if the code seems to have any leaks or i am doing something wrong.
Thanks,
Swapnil
NSAutoreleasePool should be allocated and released in the same method. Allocating it in one place and releasing it in another is usually very bad (the example code you pasted uses it correctly however)
Loading images into memory will decompress them; a 100KB compressed PNG could easily be 2MB uncompressed.
You also seem to have an extra retain call that shouldn't be there: [viewController.imagesArray retain]; (I assume the imagesArray property is marked copy or retain and not assign)
Once again I'm hunting memory leaks and other crazy mistakes in my code. :)
I have a cache with frequently used files (images, data records etc. with a TTL of about
one week and a size limited cache (100MB)). There are sometimes more then 15000 files in a
directory. On application exit the cache writes an control file with the current cache
size along with other useful information. If the applications crashes for some reason
(sh.. happens sometimes) I have in such case to calculate the size of all files on application start to make sure I know the cache size.
My app crashes at this point because of low memory and I have no clue why.
Memory leak detector does not show any leaks at all. I do not see any too. What's wrong
with the code below? Is there any other fast way to calculate the total size of all files
within a directory on iPhone? Maybe without to enumerate the whole contents of the directory?
The code is executed on the main thread.
NSUInteger result = 0;
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSDirectoryEnumerator *dirEnum = [[[NSFileManager defaultManager] enumeratorAtPath:path] retain];
int i = 0;
while ([dirEnum nextObject]) {
NSDictionary *attributes = [dirEnum fileAttributes];
NSNumber* fileSize = [attributes objectForKey:NSFileSize];
result += [fileSize unsignedIntValue];
if (++i % 500 == 0) { // I tried lower values too
[pool drain];
}
}
[dirEnum release];
dirEnum = nil;
[pool release];
pool = nil;
Thanks,
MacTouch
Draining the pool "releases" it, it doesn't just empty it. Think of autorelease pools as stacks, so you have popped yours, meaning that all these new objects are going into the main autorelease pool and not being cleaned up until it gets popped. Instead, move the creation of your autorelease pool to inside the loop. You can do something like
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
int i = 0;
while( shouldloop ) {
// do stuff
if( ++i%500 == 0 ) {
[pool drain];
pool = [[NSAutoreleasePool alloc] init];
}
}
[pool drain];
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.