// // RKEntityByAttributeCacheTest.m // RestKit // // Created by Blake Watters on 5/1/12. // Copyright (c) 2012 RestKit. All rights reserved. // #import "RKTestEnvironment.h" #import "RKEntityByAttributeCache.h" #import "RKHuman.h" #import "RKChild.h" @interface RKEntityByAttributeCacheTest : RKTestCase @property (nonatomic, strong) RKManagedObjectStore *managedObjectStore; @property (weak, nonatomic, readonly) NSManagedObjectContext *managedObjectContext; @property (nonatomic, strong) RKEntityByAttributeCache *cache; @end @implementation RKEntityByAttributeCacheTest @synthesize managedObjectStore = _managedObjectStore; @synthesize cache = _cache; - (void)setUp { [RKTestFactory setUp]; self.managedObjectStore = [RKTestFactory managedObjectStore]; NSEntityDescription *entity = [NSEntityDescription entityForName:@"Human" inManagedObjectContext:self.managedObjectContext]; self.cache = [[RKEntityByAttributeCache alloc] initWithEntity:entity attributes:@[ @"railsID" ] managedObjectContext:self.managedObjectContext]; // Disable cache monitoring. Tested in specific cases. self.cache.monitorsContextForChanges = NO; } - (void)tearDown { self.cache.monitorsContextForChanges = NO; self.cache = nil; self.managedObjectStore = nil; [RKTestFactory tearDown]; } - (NSManagedObjectContext *)managedObjectContext { return self.managedObjectStore.persistentStoreManagedObjectContext; } // NOTE: This is reliant on an implementation detail of the RKEntityByAttributeCache method `managedObjectContextDidChange:` - (void)waitForPendingChangesToProcess { [self.managedObjectStore.persistentStoreManagedObjectContext processPendingChanges]; __block BOOL processingComplete = NO; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ processingComplete = YES; }); expect(processingComplete).will.beTruthy(); } #pragma mark - Identity Tests - (void)testEntityIsAssigned { NSEntityDescription *entity = [NSEntityDescription entityForName:@"Human" inManagedObjectContext:self.managedObjectContext]; assertThat(self.cache.entity, is(equalTo(entity))); } - (void)testManagedObjectContextIsAssigned { assertThat(self.cache.managedObjectContext, is(equalTo(self.managedObjectContext))); } - (void)testAttributeNameIsAssigned { assertThat(self.cache.attributes, is(equalTo(@[ @"railsID" ]))); } #pragma mark - Loading and Flushing - (void)testLoadSetsLoadedToYes { [self.cache load]; assertThatBool(self.cache.isLoaded, is(equalToBool(YES))); } - (void)testLoadSetsCountAppropriately { RKHuman *human = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:self.managedObjectContext]; human.railsID = [NSNumber numberWithInteger:12345]; NSError *error = nil; [self.managedObjectContext save:&error]; assertThat(error, is(nilValue())); [self.cache load]; assertThatInteger([self.cache count], is(equalToInteger(1))); } - (void)testFlushCacheRemovesObjects { RKHuman *human1 = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:self.managedObjectStore.persistentStoreManagedObjectContext]; human1.railsID = [NSNumber numberWithInteger:12345]; RKHuman *human2 = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:self.managedObjectStore.persistentStoreManagedObjectContext]; human2.railsID = [NSNumber numberWithInteger:12345]; [self.managedObjectStore.persistentStoreManagedObjectContext save:nil]; [self.cache addObject:human1]; [self.cache addObject:human2]; [self.cache flush]; assertThatBool([self.cache containsObject:human1], is(equalToBool(NO))); assertThatBool([self.cache containsObject:human2], is(equalToBool(NO))); } - (void)testFlushCacheReturnsCountToZero { RKHuman *human1 = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:self.managedObjectStore.persistentStoreManagedObjectContext]; human1.railsID = [NSNumber numberWithInteger:12345]; RKHuman *human2 = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:self.managedObjectStore.persistentStoreManagedObjectContext]; human2.railsID = [NSNumber numberWithInteger:12345]; [self.managedObjectStore.persistentStoreManagedObjectContext save:nil]; [self.cache addObject:human1]; [self.cache addObject:human2]; [self.cache flush]; assertThatInteger([self.cache count], is(equalToInteger(0))); } #pragma mark - Retrieving Objects - (void)testRetrievalByNumericValue { RKHuman *human = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:self.managedObjectStore.persistentStoreManagedObjectContext]; human.railsID = [NSNumber numberWithInteger:12345]; [self.managedObjectStore.persistentStoreManagedObjectContext save:nil]; [self.cache load]; NSManagedObjectContext *childContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; childContext.parentContext = self.managedObjectContext; NSManagedObject *object = [self.cache objectWithAttributeValues:@{ @"railsID": @12345 } inContext:childContext]; assertThat(object.objectID, is(equalTo(human.objectID))); } - (void)testRetrievalOfNumericPropertyByStringValue { RKHuman *human = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:self.managedObjectStore.persistentStoreManagedObjectContext]; human.railsID = [NSNumber numberWithInteger:12345]; [self.managedObjectStore.persistentStoreManagedObjectContext save:nil]; [self.cache load]; NSManagedObjectContext *childContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; childContext.parentContext = self.managedObjectContext; NSManagedObject *object = [self.cache objectWithAttributeValues:@{ @"railsID": @"12345" } inContext:childContext]; assertThat(object, is(notNilValue())); assertThat(object.objectID, is(equalTo(human.objectID))); } - (void)testRetrievalOfObjectsWithAttributeValue { RKHuman *human1 = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:self.managedObjectStore.persistentStoreManagedObjectContext]; human1.railsID = [NSNumber numberWithInteger:12345]; RKHuman *human2 = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:self.managedObjectStore.persistentStoreManagedObjectContext]; human2.railsID = [NSNumber numberWithInteger:12345]; [self.managedObjectStore.persistentStoreManagedObjectContext save:nil]; [self.cache addObject:human1]; [self.cache addObject:human2]; NSManagedObjectContext *childContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; childContext.parentContext = self.managedObjectContext; NSSet *objects = [self.cache objectsWithAttributeValues:@{ @"railsID": @(12345) } inContext:childContext]; assertThat(objects, hasCountOf(2)); assertThat([objects anyObject], is(instanceOf([NSManagedObject class]))); } - (void)testRetrievalOfObjectsWithCollectionAttributeValue { RKHuman *human1 = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:self.managedObjectStore.persistentStoreManagedObjectContext]; human1.railsID = [NSNumber numberWithInteger:12345]; RKHuman *human2 = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:self.managedObjectStore.persistentStoreManagedObjectContext]; human2.railsID = [NSNumber numberWithInteger:5678]; [self.managedObjectStore.persistentStoreManagedObjectContext save:nil]; [self.cache addObject:human1]; [self.cache addObject:human2]; NSManagedObjectContext *childContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; childContext.parentContext = self.managedObjectContext; NSSet *objects = [self.cache objectsWithAttributeValues:@{ @"railsID": @[ @(12345), @(5678) ] } inContext:childContext]; assertThat(objects, hasCountOf(2)); assertThat([objects anyObject], is(instanceOf([NSManagedObject class]))); } - (void)testRetrievalOfObjectsWithMoreThanOneCollectionAttributeValue { NSEntityDescription *entity = [NSEntityDescription entityForName:@"Human" inManagedObjectContext:self.managedObjectContext]; self.cache = [[RKEntityByAttributeCache alloc] initWithEntity:entity attributes:@[ @"railsID", @"name" ] managedObjectContext:self.managedObjectContext]; // Disable cache monitoring. Tested in specific cases. self.cache.monitorsContextForChanges = NO; RKHuman *human1 = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:self.managedObjectStore.persistentStoreManagedObjectContext]; human1.railsID = [NSNumber numberWithInteger:12345]; human1.name = @"Blake"; RKHuman *human2 = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:self.managedObjectStore.persistentStoreManagedObjectContext]; human2.railsID = [NSNumber numberWithInteger:5678]; human2.name = @"Jeff"; RKHuman *human3 = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:self.managedObjectStore.persistentStoreManagedObjectContext]; human3.railsID = [NSNumber numberWithInteger:9999]; human3.name = @"Blake"; [self.managedObjectStore.persistentStoreManagedObjectContext save:nil]; [self.cache addObject:human1]; [self.cache addObject:human2]; [self.cache addObject:human3]; NSManagedObjectContext *childContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; childContext.parentContext = self.managedObjectContext; NSSet *objects = [self.cache objectsWithAttributeValues:@{ @"railsID": @[ @(12345), @(5678) ], @"name": @"Blake" } inContext:childContext]; // should find human1 assertThat(objects, hasCountOf(1)); assertThat([objects anyObject], is(instanceOf([NSManagedObject class]))); assertThat([[objects anyObject] objectID], equalTo([human1 objectID])); objects = [self.cache objectsWithAttributeValues:@{ @"railsID": @[ @(12345), @(9999) ], @"name": @"Blake" } inContext:childContext]; // should be human1 and human3 assertThat(objects, hasCountOf(2)); assertThat([objects valueForKey:@"objectID"], hasItems([human1 objectID], [human3 objectID], nil)); objects = [self.cache objectsWithAttributeValues:@{ @"railsID": @[ @(12345), @(9999) ], @"name": @[ @"Blake", @"Jeff" ] } inContext:childContext]; // should be human1, human2 and human3 assertThat(objects, hasCountOf(2)); assertThat([objects valueForKey:@"objectID"], hasItems([human1 objectID], [human3 objectID], nil)); objects = [self.cache objectsWithAttributeValues:@{ @"railsID": @[ @(31337), @(8888) ], @"name": @[ @"Blake", @"Jeff" ] } inContext:childContext]; // should be none assertThat(objects, hasCountOf(0)); } // Do this with 3 attributes, 2 that are arrays and 1 that is not // check // Test blowing up if you request objects without enough cache keys - (void)testAddingObjectToCache { RKHuman *human = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:self.managedObjectStore.persistentStoreManagedObjectContext]; human.railsID = [NSNumber numberWithInteger:12345]; [self.managedObjectStore.persistentStoreManagedObjectContext save:nil]; [self.cache addObject:human]; assertThatBool([self.cache containsObject:human], is(equalToBool(YES))); } - (void)testAddingObjectWithDuplicateAttributeValue { RKHuman *human1 = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:self.managedObjectStore.persistentStoreManagedObjectContext]; human1.railsID = [NSNumber numberWithInteger:12345]; RKHuman *human2 = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:self.managedObjectStore.persistentStoreManagedObjectContext]; human2.railsID = [NSNumber numberWithInteger:12345]; [self.managedObjectStore.persistentStoreManagedObjectContext save:nil]; [self.cache addObject:human1]; [self.cache addObject:human2]; assertThatBool([self.cache containsObject:human1], is(equalToBool(YES))); assertThatBool([self.cache containsObject:human2], is(equalToBool(YES))); } - (void)testRemovingObjectFromCache { RKHuman *human = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:self.managedObjectStore.persistentStoreManagedObjectContext]; human.railsID = [NSNumber numberWithInteger:12345]; [self.managedObjectStore.persistentStoreManagedObjectContext save:nil]; [self.cache addObject:human]; assertThatBool([self.cache containsObject:human], is(equalToBool(YES))); [self.cache removeObject:human]; assertThatBool([self.cache containsObject:human], is(equalToBool(NO))); } - (void)testRemovingObjectWithExistingAttributeValue { RKHuman *human1 = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:self.managedObjectStore.persistentStoreManagedObjectContext]; human1.railsID = [NSNumber numberWithInteger:12345]; RKHuman *human2 = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:self.managedObjectStore.persistentStoreManagedObjectContext]; human2.railsID = [NSNumber numberWithInteger:12345]; [self.managedObjectStore.persistentStoreManagedObjectContext save:nil]; [self.cache addObject:human1]; [self.cache addObject:human2]; assertThatBool([self.cache containsObject:human1], is(equalToBool(YES))); assertThatBool([self.cache containsObject:human2], is(equalToBool(YES))); [self.cache removeObject:human1]; assertThatBool([self.cache containsObject:human1], is(equalToBool(NO))); assertThatBool([self.cache containsObject:human2], is(equalToBool(YES))); } #pragma mark - Inspecting Cache State - (void)testContainsObjectReturnsNoForDifferingEntities { NSEntityDescription *entity = [NSEntityDescription entityForName:@"Cloud" inManagedObjectContext:self.managedObjectStore.persistentStoreManagedObjectContext]; NSManagedObject *cloud = [[NSManagedObject alloc] initWithEntity:entity insertIntoManagedObjectContext:self.managedObjectStore.persistentStoreManagedObjectContext]; assertThatBool([self.cache containsObject:cloud], is(equalToBool(NO))); } - (void)testContainsObjectReturnsNoForSubEntities { RKHuman *human = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:self.managedObjectStore.persistentStoreManagedObjectContext]; human.railsID = [NSNumber numberWithInteger:12345]; RKChild *child = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:self.managedObjectStore.persistentStoreManagedObjectContext]; child.railsID = [NSNumber numberWithInteger:12345]; [self.cache addObject:human]; assertThatBool([self.cache containsObject:human], is(equalToBool(YES))); assertThatBool([self.cache containsObject:child], is(equalToBool(NO))); } - (void)testContainsObjectWithAttributeValue { RKHuman *human = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:self.managedObjectStore.persistentStoreManagedObjectContext]; human.railsID = [NSNumber numberWithInteger:12345]; [self.managedObjectStore.persistentStoreManagedObjectContext save:nil]; [self.cache addObject:human]; assertThatBool([self.cache containsObjectWithAttributeValues:@{ @"railsID": @(12345) }], is(equalToBool(YES))); } - (void)testCount { assertThatInteger([self.cache count], is(equalToInteger(0))); RKHuman *human1 = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:self.managedObjectStore.persistentStoreManagedObjectContext]; human1.railsID = [NSNumber numberWithInteger:12345]; RKHuman *human2 = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:self.managedObjectStore.persistentStoreManagedObjectContext]; human2.railsID = [NSNumber numberWithInteger:12345]; RKHuman *human3 = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:self.managedObjectStore.persistentStoreManagedObjectContext]; human3.railsID = [NSNumber numberWithInteger:123456]; [self.cache addObject:human1]; [self.cache addObject:human2]; [self.cache addObject:human3]; assertThatInteger([self.cache count], is(equalToInteger(3))); } - (void)testCountOfAttributeValues { RKHuman *human1 = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:self.managedObjectStore.persistentStoreManagedObjectContext]; human1.railsID = [NSNumber numberWithInteger:12345]; RKHuman *human2 = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:self.managedObjectStore.persistentStoreManagedObjectContext]; human2.railsID = [NSNumber numberWithInteger:12345]; RKHuman *human3 = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:self.managedObjectStore.persistentStoreManagedObjectContext]; human3.railsID = [NSNumber numberWithInteger:123456]; [self.managedObjectStore.persistentStoreManagedObjectContext save:nil]; [self.cache addObject:human1]; [self.cache addObject:human2]; [self.cache addObject:human3]; assertThatInteger([self.cache countOfAttributeValues], is(equalToInteger(2))); } - (void)testCountWithAttributeValue { RKHuman *human1 = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:self.managedObjectStore.persistentStoreManagedObjectContext]; human1.railsID = [NSNumber numberWithInteger:12345]; RKHuman *human2 = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:self.managedObjectStore.persistentStoreManagedObjectContext]; human2.railsID = [NSNumber numberWithInteger:12345]; [self.managedObjectStore.persistentStoreManagedObjectContext save:nil]; [self.cache addObject:human1]; [self.cache addObject:human2]; assertThatInteger([self.cache countWithAttributeValues:@{ @"railsID": @(12345) }], is(equalToInteger(2))); } - (void)testThatUnloadedCacheReturnsCountOfZero { assertThatInteger([self.cache count], is(equalToInteger(0))); } #pragma mark - Lifecycle Events - (void)testManagedObjectContextProcessPendingChangesAddsNewObjectsToCache { self.cache.monitorsContextForChanges = YES; RKHuman *human1 = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:self.managedObjectStore.persistentStoreManagedObjectContext]; human1.railsID = [NSNumber numberWithInteger:12345]; [self waitForPendingChangesToProcess]; assertThatBool([self.cache containsObject:human1], is(equalToBool(YES))); } - (void)testManagedObjectContextProcessPendingChangesIgnoresObjectsOfDifferentEntityTypes { self.cache.monitorsContextForChanges = YES; RKHuman *human1 = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:self.managedObjectStore.persistentStoreManagedObjectContext]; human1.railsID = [NSNumber numberWithInteger:12345]; NSEntityDescription *entity = [NSEntityDescription entityForName:@"Cloud" inManagedObjectContext:self.managedObjectStore.persistentStoreManagedObjectContext]; NSManagedObject *cloud = [[NSManagedObject alloc] initWithEntity:entity insertIntoManagedObjectContext:self.managedObjectStore.persistentStoreManagedObjectContext]; [cloud setValue:@"Cumulus" forKey:@"name"]; [self waitForPendingChangesToProcess]; assertThatBool([self.cache containsObject:human1], is(equalToBool(YES))); assertThatBool([self.cache containsObject:cloud], is(equalToBool(NO))); } - (void)testManagedObjectContextProcessPendingChangesAddsUpdatedObjectsToCache { self.cache.monitorsContextForChanges = YES; RKHuman *human1 = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:self.managedObjectStore.persistentStoreManagedObjectContext]; human1.railsID = [NSNumber numberWithInteger:12345]; [self waitForPendingChangesToProcess]; assertThatBool([self.cache containsObject:human1], is(equalToBool(YES))); [self.cache removeObject:human1]; human1.name = @"Modified Name"; assertThatBool([self.cache containsObject:human1], is(equalToBool(NO))); [self waitForPendingChangesToProcess]; assertThatBool([self.cache containsObject:human1], is(equalToBool(YES))); } - (void)testManagedObjectContextProcessPendingChangesRemovesExistingObjectsFromCache { self.cache.monitorsContextForChanges = YES; RKHuman *human1 = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:self.managedObjectStore.persistentStoreManagedObjectContext]; human1.railsID = [NSNumber numberWithInteger:12345]; [self waitForPendingChangesToProcess]; assertThatBool([self.cache containsObject:human1], is(equalToBool(YES))); [self.managedObjectStore.persistentStoreManagedObjectContext deleteObject:human1]; [self waitForPendingChangesToProcess]; assertThatBool([self.cache containsObject:human1], is(equalToBool(NO))); } #if TARGET_OS_IPHONE - (void)testCacheIsFlushedOnMemoryWarning { RKHuman *human1 = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:self.managedObjectStore.persistentStoreManagedObjectContext]; human1.railsID = [NSNumber numberWithInteger:12345]; RKHuman *human2 = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:self.managedObjectStore.persistentStoreManagedObjectContext]; human2.railsID = [NSNumber numberWithInteger:12345]; [self.managedObjectStore.persistentStoreManagedObjectContext save:nil]; [self.cache addObject:human1]; [self.cache addObject:human2]; assertThatBool([self.cache containsObject:human1], is(equalToBool(YES))); assertThatBool([self.cache containsObject:human2], is(equalToBool(YES))); [[NSNotificationCenter defaultCenter] postNotificationName:UIApplicationDidReceiveMemoryWarningNotification object:self]; } #endif - (void)testCreatingProcessingAndDeletingObjectsWorksAsExpected { self.cache.monitorsContextForChanges = YES; [self.managedObjectStore.persistentStoreManagedObjectContext performBlockAndWait:^{ RKHuman *human1 = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:self.managedObjectStore.persistentStoreManagedObjectContext]; human1.railsID = [NSNumber numberWithInteger:12345]; RKHuman *human2 = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:self.managedObjectStore.persistentStoreManagedObjectContext]; human2.railsID = [NSNumber numberWithInteger:12345]; [self waitForPendingChangesToProcess]; assertThatBool([self.cache containsObject:human1], is(equalToBool(YES))); assertThatBool([self.cache containsObject:human2], is(equalToBool(YES))); [self.managedObjectStore.persistentStoreManagedObjectContext deleteObject:human2]; // Save and reload the cache. This will result in the cached temporary // object ID's being released during the cache flush. [self.managedObjectStore.persistentStoreManagedObjectContext save:nil]; [self.cache load]; assertThatBool([self.cache containsObject:human1], is(equalToBool(YES))); assertThatBool([self.cache containsObject:human2], is(equalToBool(NO))); }]; } #pragma mark - Compound Key Tests // missing attributes // padding nil // trying to look-up by nil // trying to lookup with empty dictionary // using weird attribute types as cache keys - (void)testEvictionOfArrayOfIdentifierAttributes { // Put some objects into the cache // Delete them RKHuman *human1 = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:self.managedObjectContext]; human1.railsID = [NSNumber numberWithInteger:12345]; RKHuman *human2 = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:self.managedObjectContext]; human2.railsID = [NSNumber numberWithInteger:56789]; [self.managedObjectContext save:nil]; [self.cache addObject:human1]; [self.cache addObject:human2]; self.cache.monitorsContextForChanges = NO; NSDictionary *attributeValues = @{ @"railsID" : @[ human1.railsID, human2.railsID ] }; [self.managedObjectContext deleteObject:human1]; [self.managedObjectContext deleteObject:human2]; [self.managedObjectContext save:nil]; [self.cache objectsWithAttributeValues:attributeValues inContext:self.managedObjectContext]; } @end