2
votes

I've got a stack trace showing a crash responding to a memory warning:

Thu Oct 28 00:42:55 iPhone DTMobileIS[10853] : _memoryNotification : {name = (null), num = 1}


Thu Oct 28 00:42:55 iPhone DTMobileIS[10853] : _memoryNotification : {
        OSMemoryNotificationLevel = 1;
        timestamp = "2010-10-28 07:42:55 GMT";
    }


Thu Oct 28 00:42:55 iPhone MyApp[11059] : Received memory warning. Level=1

Thu Oct 28 00:42:55 iPhone MyApp[11059] : -[__NSCFType tryLock]: unrecognized selector sent to instance 0x41bfd0

Thu Oct 28 00:42:55 iPhone MyApp[11059] : *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFType tryLock]: unrecognized selector sent to instance 0x41bfd0'

*** Call stack at first throw:
(
    0   CoreFoundation                      0x30897ed3 __exceptionPreprocess + 114
    1   libobjc.A.dylib                     0x3002f811 objc_exception_throw + 24
    2   CoreFoundation                      0x30899683 -[NSObject(NSObject) doesNotRecognizeSelector:] + 102
    3   CoreFoundation                      0x308411d9 ___forwarding___ + 508
    4   CoreFoundation                      0x30840f90 _CF_forwarding_prep_0 + 48
    5   CoreData                            0x316cc261 -[_NSSQLCoreConnectionObsever _purgeCaches:] + 124
    6   Foundation                          0x349bb6b3 _nsnote_callback + 142
    7   CoreFoundation                      0x3081e713 __CFXNotificationPost_old + 402
    8   CoreFoundation                      0x3081e3b3 _CFXNotificationPostNotification + 118
    9   Foundation                          0x349aadb7 -[NSNotificationCenter postNotificationName:object:userInfo:] + 70
    10  Foundation                          0x349b42d1 -[NSNotificationCenter postNotificationName:object:] + 24
    11  UIKit                               0x31ed9431 -[UIApplication _performMemoryWarning] + 48
    12  UIKit                               0x31ec7383 -[UIApplication _receivedMemoryNotification] + 126
    13  UIKit                               0x31ec72c7 _memoryStatusChanged + 42
    14  CoreFoundation                      0x3084e825 __CFNotificationCenterDarwinCallBack + 24
    15  CoreFoundation                      0x30835313 __CFMachPortPerform + 210
    16  CoreFoundation                      0x3082cce3 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 26
    17  CoreFoundation                      0x3082cca7 __CFRunLoopDoSource1 + 166
    18  CoreFoundation                      0x3081f56d __CFRunLoopRun + 520
    19  CoreFoundation                      0x3081f277 CFRunLoopRunSpecific + 230
    20  CoreFoundation                      0x3081f17f CFRunLoopRunInMode + 58
    21  GraphicsServices                    0x31e445f3 GSEventRunModal + 114
    22  GraphicsServices                    0x31e4469f GSEventRun + 62
    23  UIKit                               0x31e51123 -[UIApplication _run] + 402
    24  UIKit                               0x31e4f12f UIApplicationMain + 670
    25  MyApp                            0x00052c81 main + 72
    26  MyApp                            0x00002f18 start + 52
)

I'm not really sure what core data was doing that it tried to obtain a lock from this object, however I feel like this is indicative of a threading issue somewhere inside my code.

Can anyone think of a good way to debug this or possible root causes?

2

2 Answers

2
votes

I ran into this recently and found a solution. The bug is in Core Data itself, and manifests in versions prior to iOS 5.1 (it appears to be fixed in that version).

The crash happens when the persistent store coordinator (PSC) fails to load a given persistent store (say because the schema changed). In this case, the failed store still appears to be added to an internal Core Data cache, even though there is no external access to it. This cache is then flushed automatically by Core Data when the app is backgrounded, or in certain memory pressure situations. Everything is fine unless you the PSC that was used to try to load the failed store has since been deallocated, in which case the cache will try to access it and give the above error.

So, to summarize, you will hit this error in this situation:

  • Try and fail to load a persistent store from disk. This will likely happen because you've upgraded your app and the model schema changed.
  • Deallocate the persistent store coordinator you used to to try to load the store.
  • Background the app, or in some cases trigger a memory warning.
  • Crash

To work around this problem there are two options. I chose to simply not deallocate the PSC in the case of a failed call to -addPersistentStoreWithType:configuration:URL:options:error:, in which case the cache purge will still access a valid PSC. An alternative is to preemptively check that the model schema matches the on-disk persistent store's scheme using the following two calls:

+[NSPersistentStore metadataForPersistentStoreWithURL:error:]
-[NSManagedObjectModel isConfiguration:compatibleWithStoreMetadata:]

The second solution may avoid leaking a PSC in certain rare cases, but also assumes that the only possible way that a persistent store will fail to load is because of mismatched model schemas.

1
votes

I actually found, after a bit of code analysis, the root cause of this appears to be crossing threads with CoreData. I had a background thread from an operation queue manipulating a NSManagedObjectContext which originated on the main thread (and caused a save! ouch!).

After a few of these manipulations if I sent a memory warning in the simulator the app would crash.