views:

207

answers:

4

Update 2: Apple responded to my bug report with "We believe this issue has been addressed in iOS 4.2 b1 (8C5091e). Please let us know whether or not you continue to experience this issue with the newly released software by updating this bug report." I guess this acknowledges that the issue is in their code, not mine. I'll update with results when I or someone I know can update to try this.

Update: My coworker has reproduced the issue on a different computer and iPad, so it's probably not just a quirk with my setup.

This is strange.
I recently noticed the Leaks instrument reporting memory leaks in my application and NEVER pointing to my code in the stack traces, just various built-in libraries. Fortunately (?) I was able to strip out nearly everything and end up with a stupidly simple project that triggers this behavior every time, at least on my machine. Just to be sure, I've started a new project and gone through these steps and encountered the same behavior.

I'm using Xcode 3.2.4 and Instruments 2.7, on an iPad (non-3G) with iOS 3.2.2.

  • I start a new "Window-based Application", targeting iPad and checking the "Use Core Data for Storage" box, which generates a bunch of property accessors for handy Core Data stuff in the app delegate. (I can post these if needed, but I don't change them at all from what it generates.)
  • I edit the model to add one Entity, called "User", with no other attributes or relationships.
  • I add a -(void)loadUser method to the app delegate and call it in -(BOOL)application:didFinishLaunchingWithOptions:

Here's the only bit of customized code, as described above.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    
  // Override point for customization after application launch.

  [window makeKeyAndVisible];
  [self loadUser];

  return YES;
}

- (void)loadUser {
  NSManagedObjectContext *moc = self.managedObjectContext;
  NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
  NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"User"
                                                       inManagedObjectContext:moc];
  [fetchRequest setEntity:entityDescription];

  NSError *error;

  NSLog(@"counting users");
  NSUInteger count = [moc countForFetchRequest:fetchRequest error:&error];
  if (count == NSNotFound) {
    NSLog(@"error: %@", error);
  } else if (count == 0) {
    NSLog(@"creating user");
    // make a user
    NSManagedObject *object = [[NSManagedObject alloc] initWithEntity:entityDescription insertIntoManagedObjectContext:moc];
    if (![moc save:&error]) {
      NSLog(@"error: %@", error);
    }
    [object release];
  }

  NSLog(@"fetching user");
  NSArray *results = [moc executeFetchRequest:fetchRequest error:&error];
  if (results) {
    NSLog(@"results: %@", results);
  } else {
    NSLog(@"error: %@", error);
  }
  [fetchRequest release]; 
}

The count and insert is there because the leaks don't occur if there's no data, and this is the easiest way I know of to "populate" it. The logs are there just for straight-up sanity check; they can be removed and the leaks still show up.

This code seems completely innocuous to me. When I run it on the device with Leaks, I let it automatically check for leaks after a few seconds and it comes up with this, every time: alt text

This does not occur on the simulator, but always occurs on the device.

Clearly something bizarre is happening here. JavaScriptCore? Where does that come into things? Here's the stack trace for one of those:

16 libSystem.B.dylib thread_assign_default
15 libSystem.B.dylib _pthread_start
14 WebCore RunWebThread(void*)
13 JavaScriptCore JSC::initializeThreading()
12 libSystem.B.dylib pthread_once
11 JavaScriptCore JSC::initializeThreadingOnce()
10 JavaScriptCore WTF::initializeThreading()
 9 JavaScriptCore WTF::initializeMainThread()
 8 JavaScriptCore WTF::initializeMainThreadPlatform()
 7 libobjc.A.dylib objc_msgSend_uncached
 6 libobjc.A.dylib _class_lookupMethodAndLoadCache
 5 libobjc.A.dylib lookUpMethod
 4 libobjc.A.dylib prepareForMethodLookup
 3 libobjc.A.dylib _class_initialize
 2 libobjc.A.dylib _fetchInitializingClassList
 1 libobjc.A.dylib _objc_fetch_pthread_data
 0 libobjc.A.dylib _calloc_internal

None of the stack traces mentions my code. I'm doing no explicit multithreading. There are no web views or anything else to do with JavaScript involved. On my machine, I consistently get this result just by following the steps above. I've deleted and reinstalled the app on the iPad. I've restarted Xcode, restarted the iPad, and restarted my computer. Same deal.

My questions:
Can other people reproduce the same issue? (edit: one person has, on a different computer and iPad)

If not, I guess it's some strange problem with my configuration, and I'd like to know what I can do to fix it.

If so, what the hell is happening? Is my code at fault? Is this not how I should be fetching objects? Is it a false positive from Leaks, or a bug in Core Data? Is there a workaround? I'd really like to be able to accurately detect leaks, and this is making it hard to do so.

A: 

Well there is definitely a new thread launching. pthread are used under the hood by the Cocoa and Foundation frameworks. Also the JavaScriptCore points to something related to the webkit framework.

There are cases in which there are memory leaks in the Apple frameworks. This could be the case here. It is strange though that it does not occur in the simulator, just on the device.

What happens if you execute your -(void) loadUser method more than once. Get the memory leaks bigger or is this leak created by the initialization of the CoreData stack? What of your custom code do you need to reproduce the leak? Do you need to call your -(void) loadUser method to get the leak?

Another idea: change the persistent store type of your CoreData setup to something else to see if it is related to that. Also check if the memory leak occurs if you do not save your MOC.

GorillaPatch
I'll respond to this in multiple comments since comments are completely unformattable afaik. Yes, I need to call loadUser to trigger the leak. It is called once when the app finishes launching, as shown above.
zem
In this case, if I set up a timer to call loadUser again every ten seconds, it does not leak after the first call, but in my original app (which was vastly stripped down to end up with this test case) it would leak a little bit more every time a certain operation was performed. Even if this leak is due to something different, I can't hope to track down the persistent leak with this one periodically surfacing.
zem
The memory leak does indeed occur if I don't save. In this app, it creates and saves a User when it starts with a fresh data store, but in every subsequent startup, loadUser only fetches the existing data and doesn't call save, verifiable by the log output. The same leak occurs.
zem
I just tried changing the store type to NSBinaryStoreType rather than NSSQLiteStoreType (the default). The same leak occurs.
zem
Well it seems to me that you should file a bug at apple about that. To be honest: it is not great that it is leaking and of course you want leak-free code but as long as it is leaking so few bytes and you cannot do something obvious to fix it, I would file a bug and continue developing.
GorillaPatch
A: 

I have the same problem with this code:

// Execute the fetch -- create a mutable copy of the result.
NSError *error = nil;

NSMutableArray *mutableFetchResults = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
if (mutableFetchResults == nil) {
    // Handle the error.
}

// Set self's events array to the mutable array, then clean up.
[self setEventsArray:mutableFetchResults];
[mutableFetchResults release];

// [self setEventsArray:[[managedObjectContext executeFetchRequest:request error:&error] autorelease]]; [request release];

Leaked Object # Address Size Responsible Library Responsible Frame GeneralBlock-16 0x108fe0 16 WebCore RunWebThread(void*) GeneralBlock-16 0x1086d0 16 JavaScriptCore WTF::initializeMainThreadPlatform() GeneralBlock-16 0x1086c0 16 JavaScriptCore WTF::initializeMainThreadPlatform() GeneralBlock-16 0x1086b0 16 JavaScriptCore WTF::initializeMainThreadPlatform()

I identified fetchRequest trigger the loss of memory

rieppeta
Did you fix the leak in your case? This is unclear.
zem
No. I'm trying to correct it now.
rieppeta
anyone managed to find a solution ? I am having the exact same issue.
A: 

It could be a similar case?

https://devforums.apple.com/message/94542

rieppeta
I came across that thread while trying to track this down, but I don't think it's the same thing. It seems like everyone having that problem was specifically getting "GeneralBlock-3584" leaks, while ours are much smaller. My stacktraces also don't include pthread_create. So I can't be sure that this is a false positive based on that thread.
zem
+1  A: 

This is happening to me too, also with an application created as a window based app with core data support.

I´m getting the exact same trace using instruments to analize the app running on the iPad... I was getting crazy trying to fix the leak! It's good to know that someone else is getting this issue, we'll have to try with 4.2 then?

Maria Del Valle Duhagon
Yeah, and responses to [my other question](http://stackoverflow.com/questions/3807232/how-safe-is-it-to-upgrade-to-a-beta-ios-then-downgrade-again) indicate that I can't really try the 4.2 beta if I've only got one device, so as long as these leaks stay small I'm just going to accept them for now.
zem