My project implements the synchronization of the main data with iCloud. When I try to remove duplicates, I get the following exception:
- [NSFunctionExpression _propertyType]: unrecognized selector sent to instance
I read that the main reason for this kind of problem is the use of the same managed entity context for different threads. In my case, however, all processes are executed in the main thread. I do not know how to solve this problem.
In the initialization method, add the following observer:
[[NSNotificationCenter defaultCenter] addObserverForName:NSPersistentStoreDidImportUbiquitousContentChangesNotification object:self.managedObjectContext.persistentStoreCoordinator queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) { NSLog(@"NSPersistentStoreDidImportUbiquitousContentChangesNotification received"); [self.managedObjectContext performBlock:^{ [self.managedObjectContext mergeChangesFromContextDidSaveNotification:note]; [self filterDuplicates]; <-- Here I call method to call filtering duplicates }]; }];
Duplicate Method:
- (void) filterDuplicates { NSString *uniquePropertyKey = @"name"; NSString *entityName = @"SFGallery"; NSExpression *countExpression = [NSExpression expressionWithFormat:@"count:(%@)", uniquePropertyKey]; NSExpressionDescription *countExpressionDescription = [[NSExpressionDescription alloc] init]; [countExpressionDescription setName:@"count"]; [countExpressionDescription setExpression:countExpression]; [countExpressionDescription setExpressionResultType:NSInteger64AttributeType]; NSEntityDescription *entity = [NSEntityDescription entityForName:entityName inManagedObjectContext:self.managedObjectContext]; NSAttributeDescription *uniqueAttribute = [[entity attributesByName] objectForKey:uniquePropertyKey]; // Fetch the number of times each unique value appears in the store. NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:entityName]; fetchRequest.propertiesToFetch = @[uniqueAttribute, countExpression]; fetchRequest.propertiesToGroupBy = @[uniqueAttribute]; fetchRequest.resultType = NSDictionaryResultType; fetchRequest.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"order" ascending:YES]]; NSArray *fetchedDictionaries = [self fetchAlbumsWithFetchRequest:fetchRequest]; // Filter out unique values that have no duplicates. NSMutableArray *valuesWithDupes = [NSMutableArray array]; for (NSDictionary *dict in fetchedDictionaries) { NSNumber *count = dict[@"count"]; if ([count integerValue] > 1) { [valuesWithDupes addObject:dict[uniquePropertyKey]]; } } NSFetchRequest *dupeFetchRequest = [NSFetchRequest fetchRequestWithEntityName:entityName]; dupeFetchRequest.includesPendingChanges = NO; dupeFetchRequest.fetchBatchSize = 20; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name IN (%@)", valuesWithDupes]; dupeFetchRequest.predicate = predicate; NSArray *dupes = [self fetchAlbumsWithFetchRequest:dupeFetchRequest]; SFGallery *prevObject; for (SFGallery *duplicate in dupes) { if (prevObject) { if ([duplicate.name isEqualToString:prevObject.name]) { if ([duplicate.timestamp compare:prevObject.timestamp] == NSOrderedAscending) { [self.managedObjectContext deleteObject:duplicate]; } else { [self.managedObjectContext deleteObject:prevObject]; prevObject = duplicate; } } else { prevObject = duplicate; } } else { prevObject = duplicate; } } }
And the method when the exception occurs:
- (NSArray *)fetchAlbumsWithFetchRequest:(NSFetchRequest *)fetchRequest { Exception Here ---> NSFetchedResultsController *fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:nil]; NSError *error = nil; if (![fetchedResultsController performFetch:&error]) { NSLog(@"Error when fetching Albums: %@", [error userInfo]); return nil; } return fetchedResultsController.fetchedObjects; }
Any help?
Change 1:
I am sure this is due to NSFetchRequest. An NSFetchRequest instance has property attributes set by:
fetchRequest.propertiesToFetch = @[uniqueAttribute, countExpression];
And this is the problem of countExpression , which brings problems.
At the exception checkpoint, I registered a fetch request object, and I got the following:
<NSFetchRequest: 0x10c58b4a0> (entity: SFGallery; predicate: ((null)); sortDescriptors: (( "(timestamp, descending, compare:)" )); type: NSDictionaryResultType; includesPendingChanges: NO; propertiesToFetch: (( "(<NSAttributeDescription: 0x10c56e5c0>), name title, isOptional 1, isTransient 0, entity SFGallery, renamingIdentifier title, validation predicates (\n), warnings (\n), versionHashModifier (null)\n userInfo {\n}, attributeType 700 , attributeValueClassName NSString, defaultValue (null)", "count:(\"title\")" )); propertiesToGroupBy: (( "(<NSAttributeDescription: 0x10c56e5c0>), name title, isOptional 1, isTransient 0, entity SFGallery, renamingIdentifier title, validation predicates (\n), warnings (\n), versionHashModifier (null)\n userInfo {\n}, attributeType 700 , attributeValueClassName NSString, defaultValue (null)" )); )
This is the thrown call stack:
0 CoreFoundation 0x0000000103692495 __exceptionPreprocess + 165 1 libobjc.A.dylib 0x00000001033ca99e objc_exception_throw + 43 2 CoreFoundation 0x000000010372365d -[NSObject(NSObject) doesNotRecognizeSelector:] + 205 3 CoreFoundation 0x0000000103683d8d ___forwarding___ + 973 4 CoreFoundation 0x0000000103683938 _CF_forwarding_prep_0 + 120 5 CoreData 0x0000000100d7812d -[NSFetchRequest _newValidatedProperties:groupBy:error:] + 541 6 CoreData 0x0000000100d0da63 -[NSFetchRequest(_NSInternalMethods) _resolveEntityWithContext:] + 179 7 CoreData 0x0000000100e03c2b -[NSFetchedResultsController initWithFetchRequest:managedObjectContext:sectionNameKeyPath:cacheName:] + 555 8 myAppli 0x000000010003d2d3 -[SFCoreDataManager fetchAlbumsWithFetchRequest:] + 163 9 myAppli 0x000000010003c901 -[SFCoreDataManager filterDuplicates] + 1073 10 myAppli 0x00000001000271cb -[SFDebugViewController removeDuplicates:] + 91 11 UIKit 0x0000000101d0af06 -[UIApplication sendAction:to:from:forEvent:] + 80 12 UIKit 0x0000000101d0aeb4 -[UIApplication sendAction:toTarget:fromSender:forEvent:] + 17 13 UIKit 0x0000000101de7880 -[UIControl _sendActionsForEvents:withEvent:] + 203 14 UIKit 0x0000000101de6dc0 -[UIControl touchesEnded:withEvent:] + 530 15 UIKit 0x0000000101d41d05 -[UIWindow _sendTouchesForEvent:] + 701 16 UIKit 0x0000000101d426e4 -[UIWindow sendEvent:] + 925 17 UIKit 0x0000000101d1a29a -[UIApplication sendEvent:] + 211 18 UIKit 0x0000000101d07aed _UIApplicationHandleEventQueue + 9579 19 CoreFoundation 0x0000000103621d21 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17 20 CoreFoundation 0x00000001036215f2 __CFRunLoopDoSources0 + 242 21 CoreFoundation 0x000000010363d46f __CFRunLoopRun + 767 22 CoreFoundation 0x000000010363cd83 CFRunLoopRunSpecific + 467 23 GraphicsServices 0x000000010482cf04 GSEventRunModal + 161 24 UIKit 0x0000000101d09e33 UIApplicationMain + 1010 25 myAppli 0x0000000100010473 main + 115 26 libdyld.dylib 0x0000000103ea55fd start + 1 27 ??? 0x0000000000000001 0x0 + 1
Edit 2:
When I get the exemption: [NSFunctionExpression _propertyType]: unrecognized selector sent to instance 0x10c433d90
After printing the instance with po [0x10c433d90 class]
I get NSFunctionExpression