CoreData ContextObjectsDidChangeNotification called from another thread - ios

Recently while investigating some weird UI behavior of UIPickerView, I reached the conclusion that is was because I was reloading its components in a selector that was called observing the NSManagedObjectContextObjectsDidChangeNotification. The thing is, At least I was pretty sure this will be always be called from the main thread. But I was wrong. I'm using UIManagedDocument, and sometimes I get the following notification from another thread:
NSConcreteNotification 0x14a2664b0 {name = NSObjectsChangedInManagingContextNotification; object = <NSManagedObjectContext: 0x14a3e1be0>; userInfo = {
invalidatedAll = (
"0xd00000000d5c000e <x-coredata://B12EF0BD-E372-44D2-AF2F-8E28C5EF3E00/AdditionalAssetAttributes/p855>",
"0xd00000000d58000e <x-coredata://B12EF0BD-E372-44D2-AF2F-8E28C5EF3E00/AdditionalAssetAttributes/p854>",
"0xd00000000d5c000c <x-coredata://B12EF0BD-E372-44D2-AF2F-8E28C5EF3E00/Asset/p855>",
"0xd00000000d58000c <x-coredata://B12EF0BD-E372-44D2-AF2F-8E28C5EF3E00/Asset/p854>",
"0xd000000001d40018 <x-coredata://B12EF0BD-E372-44D2-AF2F-8E28C5EF3E00/SidecarFile/p117>",
"0xd000000001d00018 <x-coredata://B12EF0BD-E372-44D2-AF2F-8E28C5EF3E00/SidecarFile/p116>"
);
managedObjectContext = "<NSManagedObjectContext: 0x14a3e1be0>";
My question is: Does anyone knows why this notification is being called? What is the purpose of the invalidateAll elements. Also I have no Idea what AdditionalAssetAttributes or SidecarFile are.
Thanks!

I'm seeing this notification as well regularly as of iOS 11. There's not much documentation, but it seems if the notification's userInfo contains the key NSInvalidatedAllObjectsKey (invalidatedAll), then you must refetch all NSManagedObject's from the document that your application has references too. Failure to do see leaves them all no longer usable. The contents of the array can be ignored (e.g. I often find it is empty)

Related

System.MemberAccessException: Cannot create an instance of UserNotifications.UNNotificationTrigger because it is an abstract class

I am using the latest notification framework UNUserNotificationCenter for scheduling local notifications.
I am sharing some code snippets where I refer to trigger/access trigger.
Do not read this sequentially. It is just code snippets where I read or modift trigger
var trigger = UNCalendarNotificationTrigger.CreateTrigger(notification.ScheduledTime.DateTimeToNSDateComponents(), false);
trigger.DateComponents.Hour = trigger.DateComponents.Hour + 7;
var trigger = (UNCalendarNotificationTrigger)(notification.Trigger);
This works without any compilation error and the notification also worked. We have this code in the app store from past few months. Off late we are seeing a crash with the below message . I haven't referenced UNNotificationTrigger in my code. BTW this is xamarin IOS app.
System.MemberAccessException: Cannot create an instance of UserNotifications.UNNotificationTrigger because it is an abstract class
This is a problem in Xamarin.iOS that we are looking to fix.
What is going on is that the trigger vanishes or gets collected at some point, and it is later resurfaced, and the resurfacing does not know about the actual hidden concrete implementation for this, so you get the above message.
The best workaround for now is to keep a pointer to the trigger alive in managed code, thus ensuring that the resurfacing is never triggered.
We are tracking this here:
https://github.com/xamarin/xamarin-macios/issues/3935#issuecomment-381200652

AVPlayer removing observer crash in Swift 2.2

I have a video app that I built a while back in Swift 1 and I've been trying to migrate to Swift 2.2. It all (finally) works apart from a weird crash to do with observers.
func removeObservers()
{
print("REMOVING OBSERVERS")
if ( !self.is_image && self.player != nil ) {
if (self.player?.observationInfo != nil) {
self.player?.removeObserver(self, forKeyPath: "currentItem.status")
self.player?.removeObserver(self, forKeyPath: "readyForDisplay")
}
}
NSNotificationCenter.defaultCenter().removeObserver(self)
}
This worked previously using SwiftTryCatch but with the lines in place crashes with "'Cannot remove an observer for the key path "readyForDisplay" from because it is not registered as an observer.'" OR with that an observer is registered on a deallocated object if I comment it out.
If I add a do { } catch {} to it I get an error that "this does not throw" and it just crashes the same. How do I go about putting this in some form of try-catch format?
In Swift 2, the libs got annoyingly strict about errors that are truly unexpected (which throw) versus errors that the programmer could have prevented (which do not throw, but just crash your app).
(I’m not a fan of this distinction, or at least not of all the specific decisions Apple made about which errors fall in which category. The JSON API verges on the nonsensical in this department. But…we work with the API we’ve got.)
The NSKeyValueObserving docs say:
It is an error to call removeObserver:forKeyPath: if the object has not been registered as an observer.
“It is an error” is Apple code for “you are responsible for never doing this, and if you do, your app will crash in an uncatchable way.”
In these situations, there is usually an API call you can make to check the validity of the thing you’re about to do. However, AFAIK, there’s no KVO API call you can make to ask, “Is X observing key path Y of object Z?” That means you have three options:
Figure out why you’re trying to remove an observer from something you’re not observing, and prevent that using your program’s own internal logic.
Keep a weak instance var for “player I’m observing,” and check that for a match before attempting to remove the observer.
Add self as an observer before removing it. (I’m pretty sure that a redundant add is OK.)
Since you are making a call removeObserver(self) at the end of the method, why cant you uncomment above code? Because removeObserver(self) removes all the observers if registered any. I hope this solves your issue.
NSNotificationCenter.defaultCenter().removeObserver(self)
status is a property of either AVPlayer or AVPlayerItem.
readyForDisplay is a property of AVPlayerLayer

Saving NSManagedObjectContext on Stackmob -- uploads to server, but crashes app

I have been running into an issue in using StackMob as the backend of my iOS application (though I'm not sure if this is an issue in wrongly using StackMob's methods or an iOS issue).
I am allowing a user to create a post object that is just a subclassed NSManagedObject, and uploading that to the server to be used in other parts of the application. The issue that arises occurs in the method:
[NSManagedObjectContext saveOnSuccess:<^(void)successBlock> onFailure:<^(NSError *error)failureBlock>];
Here, I am using a StackMob method for asynchronously saving the MOC found in the NSManagedObjectContext(Concurrency) Category Reference.
The view before this one performs a fetch on recent posts, and in the case where the fetch is not performed posting works fine, but if a fetch was performed then in saving the MOC in order to upload the new post I receive the following output as an error message:
2013-09-11 17:08:09.284 imageTagging[1824:1843] -[__NSDictionaryI bytes]: unrecognized
selector sent to instance 0x1e3123d0
2013-09-11 17:08:09.291 imageTagging[1824:1843] *** Terminating app due to uncaught
exception 'NSInvalidArgumentException', reason: '-[__NSDictionaryI bytes]: unrecognized
selector sent to instance 0x1e3123d0'
*** First throw call stack:
(0x318cb3e7 0x395c6963 0x318cef31 0x318cd64d 0x31825208 0x321631cf 0x3216b991 0x15ea99
0x318c8757 0x15e109 0x15dabf 0x10d1c3 0x318d05b7 0x10cd4d 0x10c829 0x10923b 0x1076d9
0x3166c431 0x316c44d1 0x1685c3 0x316c7e5d 0x399e3b3b 0x399e167d 0x399e4613 0x399e47d9
0x39a087f1 0x39a08684)
libc++abi.dylib: terminate called throwing an exception
(lldb)
The data is still uploading to the StackMob server, and can be called upon when the app is run later -- but the app crashes in trying to save it. All of this is performed in the view controller. I've tried to enforce all MOC saves to be performed on the main thread, but the error still occurs. I've also tried dispatching a "save queue" and updating the UI after save completes. This method seemed to work for a bit, but then the errors came up again (may have just been a fluke). I also tried to do this with the synchronous save calls in the documentation
The same error occurs when trying to perform other saves as well (such as after creating a new user or when updating a user's information), and all come down to the same function call causing the problems. It may also be worthwhile to note that the error is always the same (specifically that a type __NSDictionaryI is trying to access its unrecognized selector bytes.
Here is the full method call with the input parameters filled out:
//save context
[[[[SMClient defaultClient] coreDataStore] contextForCurrentThread] saveOnSuccess:^{
NSLog(#"You created a new Post object!");
[[[[SMClient defaultClient] coreDataStore] contextForCurrentThread] refreshObject:newPost mergeChanges:YES];
NSLog(#"refreshed");
} onFailure:^(NSError *error) {
NSLog(#"There was an error! %#", error);
}];
UPDATE: I have narrowed down the problem to a mishandling of information returned from a fetch performed by the previous view controller. Specifically, it occurs after the results are fetched in trying to use the data to update.
As a result of this new insight, the question I am really facing is how to properly save a managed object in the context after a fetch. I believe StackMob takes care of creating the managed objects after the fetch (i.e. server query). I've tried creating a new object from the results array (each "obj" is an NSManagedObject) with:
[results enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSManagedObject *newObj = obj;
}];
I've also tried referencing the fetched results by object id (each "obj" is an objectID) with:
[results enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSManagedObject *newObj = [self.managedObjectContext objectWithID:obj];
}];
Any insight on how to properly do this would be greatly appreciated!
UPDATE 2: It looks like the error is actually occurring in trying to use and save geolocation data. In order to use the queried objects' geolocation data, it must be unarchived -- but to save it, it must be archived. I'm looking into how to do this now, and if I come across a good solution, I'll update again.
FINAL UPDATE: Got it figured out! It turns out that the issue I was having was that I was unarchiving the geolocation data to update the UI and do some calculations, and while I was archiving it again to be stored properly, I created an annotation on a map that referenced the unarchived data. As a result, the MOC maintained the data that could not be saved via the StackMob methods. By only saving the archived data, I can save as often as I'd like and just unarchive the geodata when it needs to be used. Problem solved!
Please feel free to comment if anyone comes across a similar problem and needs some insight or references!
I am just going to put my final update in here as an answer, since it explains how I solved the issue.
FINAL UPDATE: Got it figured out! It turns out that the issue I was having was that I was unarchiving the geolocation data to update the UI and do some calculations, and while I was archiving it again to be stored properly, I created an annotation on a map that referenced the unarchived data. As a result, the MOC maintained the data that could not be saved via the StackMob methods. By only saving the archived data, I can save as often as I'd like and just unarchive the geodata when it needs to be used. Problem solved!
Please feel free to comment if anyone comes across a similar problem and needs some insight or references!
Moral of the story, if you are running into issues similar to this, make sure you are not (even if you don't mean to be) storing references to the unarchived SMGeoPoint data in any of your managed objects. It is trying to store those that causes the problem.

Merging Changes between NSManagedObjectContexts (Multithreaded)

I have a problem/crash merging the data of different NSManagedObjectContexts (iOS 6.1, Xcode 4.6).
Most of the time the error that rises is the following:
CoreData: error: Serious application error. Exception was caught during Core Data change processing. This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification. statement is still active with userInfo (null)
One time i got this error:
An observer of NSManagedObjectContextDidSaveNotification illegally threw an exception. Objects saved = { $OBJLIST } and exception = statement is still active with userInfo = (null)
Sadly there is no value in the stacktraces i got. They just show symbols that are CoreData internal (if any).
Our CoreData stack:
1 NSPersistentStoreLocator shared by all threads
1 unique MOC per thread ( created on first need )
All MOCs are saved in a Dictionary
An observer is added for the notification NSManagedObjectContextDidSaveNotification to update the MOCs when one is saving to the store. The defined selector is calling mergeChangesFromContextDidSaveNotification on every other thread/context except the one that did the save operation.
+ (void)mergeChanges:(NSNotification *)notification {
NSManagedObjectContext *ctx;
for ( NSNumber *threadId in [__managedObjectContexts keyEnumerator] ) {
ctx = [__managedObjectContexts objectForKey:threadId];
if ( notification.object != ctx ) {
[ctx mergeChangesFromContextDidSaveNotification:notification];
}
}
}
Steps to produce the error:
In a background thread CoreData data objects that arent needed anymore (unreferenced by other objects) are being deleted.
[[CDUtils managedObjectContext] deleteObject:obj];
[[CDUtils managedObjectContext] save:&error];
While this is happening the user can navigate throughout the application. Userinteraction (i.e. opening a tableview) can trigger executeFetch calls on the moc of the main thread.
Every thread uses the same NSPersistentStoreLocator but a different/unique MOC.
We tried different methods of locking with NSLocks and the lock on the NSPersistentStoreLocator for threadsafety. I.e. enclosing the mergeChanges Method and the save operation each by a lock/unlock or enclosing both methods in the same lock/unlock. Sadly we had no succes thus far.
[__storeCoordinator lock];
[__storeCoordinator unlock];
I'd be thankful for every piece of advice you can give me to approach a solution. Thank you for your time!
For the people interested. I managed to make things work with multiple threads / MOCs. I basicly solved the original problem / those errors i had by locking PSC and MOCs correctly. The next problem that arose was how to know if its save to mergeChanges on a context. I cant lock nor should mergeChanges on a MOC that has no running thread anymore. But how do i know if the thread is running or not? If i just check for NSThreads "isExecuting"-Method it might happen that the thread exits just after i checked the BOOL. Im trying an easier approach now where i just merge into the main thread.

I can't make a new instance of NSManagedObject on background thread with non-main MOC

I have researched a ton of posts regarding Core Data on background threads, and I feel like I understand (on paper) what needs to be going on. I guess we'll see. I am working on migrating an existing OS X app to Core Data, and am having issues making new instances of my NSManagedObject on an async thread.
Here is a sample of the code I am running right after I have moved onto a background thread:
NSLog(#"JSON 1");
NSManagedObjectContext * context = [[NSManagedObjectContext alloc] init];
[context setPersistentStoreCoordinator:[[NSApp delegate] persistentStoreCoordinator]];
asset = (MTAssetInfo*)[NSEntityDescription insertNewObjectForEntityForName:#"Info" inManagedObjectContext:context];
NSLog(#"JSON 2");
The result is that the first log message (#"JSON 1") gets called 31 times, and the second one (#"JSON 2") is never called. The object isn't being made and returned correctly.
The model for this Info entity is quite complex with a few transformable attributes that may or may not be setup correctly. The weird thing is that similar code run on the main thread and the main MOC works great. No issues.
EDIT - Some more context
The async call originates from here:
for (NSNumber *sectionID in sectionsToShow) {
dispatch_group_async(group, queue, ^{
MTAssetInfo *asset = [self assetWithRefID:[sectionID unsignedIntegerValue]];
if (asset != nil) {
[sectionsLock lock];
[sectionsTemp addObject:asset];
[sectionsLock unlock];
}
});
}
The assetWithRefID method never returns with an object because of the other code snippet. It never successfully pulls an NSManagedObject out of the context on the background thread.
You are going to have to provide more information to get real help, but I bet your problem is an error happening in the NSManagedDocument background thread.
I'd register a NSNotificationCenter for ALL messages (name:nil object:nil) and just print them out. I bet you see a status change or error message in there that is failing.
You might want to try a #try/#catch block around it just to see if exceptions are being thrown.
Maybe it will give you more to go on.
One other suggestion... Swizzling isn't necessarily the right tool for production stuff, but it's almost unbeatable for debugging. I have method-swizled several entire classes, so that it sends a detailed NSNotification before/after each invocation.
It has saved me tons of time, and helped me track down some wicked bugs. Now, when something is going on in CoreData, I take out my set of classes, link them in, and see all the detail I want.
I know that does not exactly answer you question, but hopefully it will put you on the track so you can provide some more information and get it all fixed.
If that's too much for you, create a subclass and instantiate that, with a similar method for calling super. You can get a real idea of the entire flow pretty easily.

Resources