iPhone core data - fetched managed objects not being autoreleased on device (fine on simulator)
I'm currently struggling with a core data issue with my app that defies (my) logic. I'm sure I'm doing something wrong but can't see what. I am doing a basic executeFetchRequest on my core data entity, but the array of managed objects returned never seems to be released ONLY when I run it on the iPhone, under the simulator it works exactly as expected. This is despite using an NSAutoreleasePool to ensure the memory footprint is minimised. I have also checked with Instruments and there are no leaks, just ever increasing allocations of memory (by '[NSManagedObject(_PFDynamicAccessorsAndPropertySupport) allocWithEntity:]'). In my actual app this eventually leads to a didReceiveMemoryWarning call. I have produced a minimal program that reproduces the problem below. I have tried various things such as faulting all the objects before draining the pool, but with no joy. If I provide an NSError pointer to the fetch no error is returned. There are no background threads running.
+(natural_t) get_free_memory { mach_port_t host_port; mach_msg_type_number_t host_size; vm_size_t pagesize; host_port = mach_host_self(); host_size =sizeof(vm_statistics_data_t)/sizeof(integer_t); host_page_size(host_port,&pagesize); vm_statistics_data_t vm_stat; if(host_statistics(host_port, HOST_VM_INFO,(host_info_t)&vm_stat,&host_size)!= KERN_SUCCESS){ NSLog(@"Failed to fetch vm statistics"); return0; } /* Stats in bytes */ natural_t mem_free = vm_stat.free_count * pagesize; return mem_free;}-(void)viewDidLoad{ [super viewDidLoad]; // Set up the edit and add buttons. self.navigationItem.leftBarButtonItem = self.editButtonItem; UIBarButtonItem*addButton =[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(insertNewObject)]; self.navigationItem.rightBarButtonItem = addButton; [addButton release]; // Obtain the Managed Object Context NSManagedObjectContext*context =[(id)[[UIApplication sharedApplication] delegate] managedObjectContext]; // Check the free memory before we start NSLog(@"INITIAL FREEMEM: %d",[RootViewController get_free_memory]); // Loop around a few times for(int i=0; i<20; i++){ // Create an autorelease pool just for this loop NSAutoreleasePool*looppool =[[NSAutoreleasePool alloc] init]; // Check the free memory each time around the loop NSLog(@"FREEMEM: %d",[RootViewController get_free_memory]); // Create a minimal request NSEntityDescription*entityDescription =[NSEntityDescription
entityForName:@"TestEntity" inManagedObjectContext:context]; // 'request' released after fetch to minimise use of autorelease pool
NSFetchRequest*request =[[NSFetchRequest alloc] init]; [request setEntity:entityDescription]; // Perform the fetch NSArray*array =[context executeFetchRequest:request error:nil];
[request release]; // Drain the pool - should release the fetched managed objects? [looppool drain]; } // Check the free menory at the end NSLog(@"FINAL FREEMEM: %d",[RootViewController get_free_memory]);}
When I run the above on the simulator I get the following output (which looks reasonable to me):
2011-06-0609:50:28.123 renniksoft[937:207] INITIAL FREEMEM:147824642011-06-0609:50:28.128 renniksoft[937:207] FREEMEM:148070402011-06-0609:50:28.135 renniksoft[937:207] FREEMEM:148316162011-06-0609:50:28.139 renniksoft[937:207] FREEMEM:148520962011-06-0609:50:28.142 renniksoft[937:207] FREEMEM:148725762011-06-0609:50:28.146 renniksoft[937:207] FREEMEM:148971522011-06-0609:50:28.149 renniksoft[937:207] FREEMEM:149176322011-06-0609:50:28.153 renniksoft[937:207] FREEMEM:149381122011-06-0609:50:28.158 renniksoft[937:207] FREEMEM:149626882011-06-0609:50:28.161 renniksoft[937:207] FREEMEM:149831682011-06-0609:50:28.165 renniksoft[937:207] FREEMEM:147415042011-06-0609:50:28.168 renniksoft[937:207] FREEMEM:147701762011-06-0609:50:28.174 renniksoft[937:207] FREEMEM:147906562011-06-0609:50:28.177 renniksoft[937:207] FREEMEM:148111362011-06-0609:50:28.182 renniksoft[937:207] FREEMEM:148316162011-06-0609:50:28.186 renniksoft[937:207] FREEMEM:145899522011-06-0609:50:28.189 renniksoft[937:207] FREEMEM:146104322011-06-0609:50:28.192 renniksoft[937:207] FREEMEM:146309122011-06-0609:50:28.194 renniksoft[937:207] FREEMEM:146513922011-06-0609:50:28.197 renniksoft[937:207] FREEMEM:146718722011-06-0609:50:28.200 renniksoft[937:207] FREEMEM:146923522011-06-0609:50:28.203 renniksoft[937:207] FINAL FREEMEM:14716928
However, when I run it on an actual iPhone 4 (4.3.3) I get the following result:
2011-06-0609:55:54.341 renniksoft[4727:707] INITIAL FREEMEM:2679275522011-06-0609:55:54.348 renniksoft[4727:707] FREEMEM:2679521282011-06-0609:55:54.702 renniksoft[4727:707] FREEMEM:2658181122011-06-0609:55:55.214 renniksoft[4727:707] FREEMEM:2653552642011-06-0609:55:55.714 renniksoft[4727:707] FREEMEM:2648924162011-06-0609:55:56.215 renniksoft[4727:707] FREEMEM:2644418562011-06-0609:55:56.713 renniksoft[4727:707] FREEMEM:2639790082011-06-0609:55:57.226 renniksoft[4727:707] FREEMEM:2640896002011-06-0609:55:57.721 renniksoft[4727:707] FREEMEM:2636308482011-06-0609:55:58.226 renniksoft[4727:707] FREEMEM:2631680002011-06-0609:55:58.726 renniksoft[4727:707] FREEMEM:2627051522011-06-0609:55:59.242 renniksoft[4727:707] FREEMEM:2628526082011-06-0609:55:59.737 renniksoft[4727:707] FREEMEM:2623897602011-06-0609:56:00.243 renniksoft[4727:707] FREEMEM:2619310082011-06-0609:56:00.751 renniksoft[4727:707] FREEMEM:2619924482011-06-0609:56:01.280 renniksoft[4727:707] FREEMEM:2615746562011-06-0609:56:01.774 renniksoft[4727:707] FREEMEM:2611486722011-06-0609:56:02.290 renniksoft[4727:707] FREEMEM:2607554562011-06-0609:56:02.820 renniksoft[4727:707] FREEMEM:2608373762011-06-0609:56:03.334 renniksoft[4727:707] FREEMEM:2603950082011-06-0609:56:03.825 renniksoft[4727:707] FREEMEM:2599321602011-06-0609:56:04.346 renniksoft[4727:707] FINAL FREEMEM:259555328
The amount of free memory reduces each time round the loop in proportion to the managed objects I fetch e.g. if I fetch twice as many objects then the free memory reduces twice as quickly - so I'm pretty confident it is the managed objects that are not being released. Note that the entities that are being fetched are very basic, just two attributes, a string and a 16 bit integer. There are 1000 of them being fetched in the examples above. The code I used to generate them is as follows:
// Create test entitiesfor(int i=0; i<1000; i++){ id entity =[NSEntityDescription insertNewObjectForEntityForName:@"TestEntity" inManagedObjectContext:context]; [entity setValue:[NSString stringWithFormat:@"%d",i] forKey:@"name"]; [entity setValue:[NSNumber numberWithInt:i] forKey:@"value"];}if(![context save:nil]){ NSLog(@"Couldn't save");}
If anyone can explain to me what is going on I'd be very grateful! This issue is the only only one holding up the release of my app. It works beautifully on the simulator!!
Please let me know if there's any more info I can supply.
iPhone 4, iOS 4.3.3