It's hard to believe, but when using the run argument "-com.apple.CoreData.ConcurrencyDebug 1", the app would crash consistently just because its name (PRODUCT_NAME variable in Xcode build settings) starts with "Music" or "Musik". Simply changing the target name prevents the crash.
This issue started appearing only on iOS 14.0
To reproduce:
Use Xcode 12.01 to create a new project -> iOS -> App. Use Core Data and Objective-c in the wizard settings.
This issue started appearing only on iOS 14.0
To reproduce:
Use Xcode 12.01 to create a new project -> iOS -> App. Use Core Data and Objective-c in the wizard settings.
Enter an app name which is starting with "Music" or "Musik". For example "MusicTest".
Place "-com.apple.CoreData.ConcurrencyDebug 1" in the scheme run arguments.
Add a new entity to the data model named "TestEnt"
Add the following code to the end of the AppDelegate.m file.
Code Block - (void)test { // --- // Set run argument in the scheme as follow: // -com.apple.CoreData.ConcurrencyDebug 1 // --- NSLog(@"TEST"); dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{ NSManagedObjectContext *managedObjectContext = [self managedObjectContext]; [managedObjectContext performBlockAndWait:^{ NSError *error = nil; NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"TestEnt"]; NSArray *result = [managedObjectContext executeFetchRequest:request error:&error]; NSLog(@"result=%@", result); }]; }); } - (NSManagedObjectContext *)managedObjectContext { // The old-fashion way to create a managed object conttext NSString *documentsDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; NSString *storeFilePath = [[documentsDir stringByAppendingPathComponent:@"MusicTest"] stringByAppendingPathExtension:@"sqlite"]; NSURL *storeURL = [NSURL fileURLWithPath:storeFilePath]; NSURL *objectModelURL = [[NSBundle mainBundle] URLForResource:@"MusicTest" withExtension:@"momd"]; NSManagedObjectModel *objectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:objectModelURL]; NSPersistentStoreCoordinator *coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:objectModel]; NSDictionary *options = @{NSMigratePersistentStoresAutomaticallyOption: @YES, NSInferMappingModelAutomaticallyOption: @YES}; NSError *error = nil; [coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error]; NSManagedObjectContext *managedObjectContext = nil; if (coordinator != nil) { managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; managedObjectContext.persistentStoreCoordinator = coordinator; managedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy; } return managedObjectContext; }
Modify code on ViewController.m as follow:
Code Block - (void)viewDidLoad { [super viewDidLoad]; [(AppDelegate *)UIApplication.sharedApplication.delegate test]; }
Run project and view crash coming.
Code Block Multithreading_Violation_AllThatIsLeftToUsIsHonor
Select MusicTest project on the project navigator.
Rename "MusicTest" appearing under "TARGETS" on the main window to "MyMusicTest"
Run project again
No crash this time.
I get the same result. This looks like an internal issue that probably affects at least some other app names that begin with the same letters as Apple's iOS apps.
Here's a workaround that may help. Look at the full stack trace when the assertion happens:
Notice that frames near the middle of the pile mention autoreleasing. That suggests a course of action, since if you can affect how things are autoreleased in the framework, you might change the behavior. And in fact if you add @autoreleasepool to your performBlockAndWait:
...the violation goes away. I don't know if this will help in your real code as well as in your demo code, but it may be worth a try.
Here's a workaround that may help. Look at the full stack trace when the assertion happens:
Code Block * thread #2, queue = 'com.apple.root.user-initiated-qos', stop reason = EXC_BREAKPOINT (code=1, subcode=0x184e68d68) * frame #0: 0x0000000184e68d68 CoreData`+[NSManagedObjectContext Multithreading_Violation_AllThatIsLeftToUsIsHonor] + 12 frame #1: 0x0000000184e68c4c CoreData`_PFAssertSafeMultiThreadedAccess_impl + 548 frame #2: 0x0000000184e68a24 CoreData`__68-[NSManagedObjectContext _PFAutoreleasePoolReferenceQueueTrampoline]_block_invoke + 92 frame #3: 0x0000000184e32b2c CoreData`-[_PFAutoreleasePoolThunk dealloc] + 40 frame #4: 0x00000001801858e8 libobjc.A.dylib`AutoreleasePoolPage::releaseUntil(objc_object**) + 204 frame #5: 0x00000001801857b8 libobjc.A.dylib`objc_autoreleasePoolPop + 236 frame #6: 0x00000001024f8fac libdispatch.dylib`_dispatch_last_resort_autorelease_pool_pop + 40 frame #7: 0x000000010250b37c libdispatch.dylib`_dispatch_root_queue_drain + 1428 frame #8: 0x000000010250b928 libdispatch.dylib`_dispatch_worker_thread2 + 136 frame #9: 0x00000001bad2175c libsystem_pthread.dylib`_pthread_wqthread + 212
Notice that frames near the middle of the pile mention autoreleasing. That suggests a course of action, since if you can affect how things are autoreleased in the framework, you might change the behavior. And in fact if you add @autoreleasepool to your performBlockAndWait:
Code Block [managedObjectContext performBlockAndWait:^{ @autoreleasepool { NSError *error = nil; NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"TestEnt"]; NSArray *result = [managedObjectContext executeFetchRequest:request error:&error]; NSLog(@"result=%@", result); } }];
...the violation goes away. I don't know if this will help in your real code as well as in your demo code, but it may be worth a try.