NSManagedObjectContext.m
1 /* Copyright(c)2008 Dan Knapp 2 3 Permission is hereby granted, free of charge, to any person obtaining a copy of 4 this software and associated documentation files(the "Software"), to deal in the 5 Software without restriction, including without limitation the rights to use, 6 copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the 7 Software, and to permit persons to whom the Software is furnished to do so, 8 subject to the following conditions: 9 10 The above copyright notice and this permission notice shall be included in all 11 copies or substantial portions of the Software. 12 13 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ 19 #import "NSEntityDescription-Private.h" 20 #import "NSManagedObject-Private.h" 21 #import "NSManagedObjectID-Private.h" 22 #import "NSPersistentStoreCoordinator-Private.h" 23 #import <CoreData/CoreDataErrors.h> 24 #import <CoreData/NSAtomicStore.h> 25 #import <CoreData/NSEntityDescription.h> 26 #import <CoreData/NSFetchRequest.h> 27 #import <CoreData/NSManagedObject.h> 28 #import <CoreData/NSManagedObjectContext.h> 29 #import <CoreData/NSManagedObjectModel.h> 30 #import <CoreData/NSPersistentStore.h> 31 #import <Foundation/NSRaise.h> 32 #import <Foundation/NSUndoManager.h> 33 34 NSString *const NSManagedObjectContextWillSaveNotification = 35 @"NSManagedObjectContextWillSaveNotification"; 36 NSString *const NSManagedObjectContextDidSaveNotification = 37 @"NSManagedObjectContextDidSaveNotification"; 38 39 NSString *const NSInsertedObjectsKey = @"NSInsertedObjectsKey"; 40 NSString *const NSUpdatedObjectsKey = @"NSUpdatedObjectsKey"; 41 NSString *const NSDeletedObjectsKey = @"NSDeletedObjectsKey"; 42 NSString *const NSRefreshedObjectsKey = @"NSRefreshedObjectsKey"; 43 NSString *const NSInvalidatedObjectsKey = @"NSInvalidatedObjectsKey"; 44 NSString *const NSInvalidatedAllObjectsKey = @"NSInvalidatedAllObjectsKey"; 45 NSString *const NSAffectedObjectsErrorKey = @"NSAffectedObjectsErrorKey"; 46 47 @interface NSAtomicStore (private) 48 - (void) _uniqueObjectID: (NSManagedObjectID *) objectID; 49 @end 50 51 @implementation NSManagedObjectContext 52 53 - init { 54 _lock = [[NSLock alloc] init]; 55 _storeCoordinator = nil; 56 _undoManager = [[NSUndoManager alloc] init]; 57 _registeredObjects = [[NSMutableSet alloc] init]; 58 _insertedObjects = [[NSMutableSet alloc] init]; 59 _updatedObjects = [[NSMutableSet alloc] init]; 60 _deletedObjects = [[NSMutableSet alloc] init]; 61 62 _objectIdToObject = NSCreateMapTable(NSObjectMapKeyCallBacks, 63 NSObjectMapValueCallBacks, 0); 64 _requestedProcessPendingChanges = NO; 65 return self; 66 } 67 68 - (void) dealloc { 69 [_storeCoordinator release]; 70 [_undoManager release]; 71 [_registeredObjects release]; 72 [_insertedObjects release]; 73 [_objectIdToObject release]; 74 [super dealloc]; 75 } 76 77 - (NSPersistentStoreCoordinator *) persistentStoreCoordinator { 78 return _storeCoordinator; 79 } 80 81 - (NSUndoManager *) undoManager { 82 return _undoManager; 83 } 84 85 - (BOOL) retainsRegisteredObjects { 86 return _retainsRegisteredObjects; 87 } 88 89 - (BOOL) propagatesDeletesAtEndOfEvent { 90 return _propagatesDeletesAtEndOfEvent; 91 } 92 93 - (NSTimeInterval) stalenessInterval { 94 return _stalenessInterval; 95 } 96 97 - mergePolicy { 98 return _mergePolicy; 99 } 100 101 - (void) persistentStoresDidChange: (NSNotification *) note { 102 NSArray *stores = 103 [[note userInfo] objectForKey: NSRemovedPersistentStoresKey]; 104 105 for (NSPersistentStore *store in stores) { 106 NSArray *allObjects = NSAllMapTableValues(_objectIdToObject); 107 108 for (NSManagedObject *check in allObjects) { 109 NSManagedObjectID *objectID = [check objectID]; 110 111 if ([objectID persistentStore] == store) { 112 113 NSEntityDescription *entity = [check entity]; 114 NSArray *properties = [[entity propertiesByName] allKeys]; 115 116 for (NSString *key in properties) 117 [check removeObserver: self forKeyPath: key]; 118 119 [_registeredObjects removeObject: check]; 120 [_insertedObjects removeObject: check]; 121 [_updatedObjects removeObject: check]; 122 [_deletedObjects removeObject: check]; 123 NSMapRemove(_objectIdToObject, objectID); 124 } 125 } 126 } 127 } 128 129 - (void) setPersistentStoreCoordinator: (NSPersistentStoreCoordinator *) value { 130 if (_storeCoordinator != nil) 131 [[NSNotificationCenter defaultCenter] 132 removeObserver: self 133 name: NSPersistentStoreCoordinatorStoresDidChangeNotification 134 object: _storeCoordinator]; 135 136 value = [value retain]; 137 [_storeCoordinator release]; 138 _storeCoordinator = value; 139 140 if (_storeCoordinator != nil) 141 [[NSNotificationCenter defaultCenter] 142 addObserver: self 143 selector: @selector(persistentStoresDidChange:) 144 name: NSPersistentStoreCoordinatorStoresDidChangeNotification 145 object: _storeCoordinator]; 146 } 147 148 - (void) setUndoManager: (NSUndoManager *) value { 149 value = [value retain]; 150 [_undoManager release]; 151 _undoManager = value; 152 } 153 154 - (void) setRetainsRegisteredObjects: (BOOL) value { 155 _retainsRegisteredObjects = value; 156 // FIXME: actually release/retain if needed 157 NSUnimplementedMethod(); 158 } 159 160 - (void) setPropagatesDeletesAtEndOfEvent: (BOOL) value { 161 _propagatesDeletesAtEndOfEvent = value; 162 } 163 164 - (void) setStalenessInterval: (NSTimeInterval) value { 165 _stalenessInterval = value; 166 } 167 168 - (void) setMergePolicy: value { 169 value = [value retain]; 170 [_mergePolicy release]; 171 _mergePolicy = value; 172 } 173 174 - (NSSet *) registeredObjects { 175 return _registeredObjects; 176 } 177 178 - (NSSet *) insertedObjects { 179 return _insertedObjects; 180 } 181 182 - (NSSet *) updatedObjects { 183 return _updatedObjects; 184 } 185 186 - (NSSet *) deletedObjects { 187 return _deletedObjects; 188 } 189 190 - (void) _setHasChanges: (BOOL) value { 191 _hasChanges = value; 192 } 193 194 - (BOOL) hasChanges { 195 return _hasChanges; 196 } 197 198 - (void) lock { 199 [_lock lock]; 200 } 201 202 - (void) unlock { 203 [_lock unlock]; 204 } 205 206 - (BOOL) tryLock { 207 return [_lock tryLock]; 208 } 209 210 - (void) undo { 211 NSUnimplementedMethod(); 212 } 213 214 - (void) redo { 215 NSUnimplementedMethod(); 216 } 217 218 - (void) reset { 219 NSUnimplementedMethod(); 220 } 221 222 - (void) rollback { 223 NSUnimplementedMethod(); 224 } 225 226 - (NSAtomicStoreCacheNode *) _cacheNodeForObjectID: 227 (NSManagedObjectID *) objectID 228 { 229 NSAtomicStore *store = (NSAtomicStore *) [_storeCoordinator 230 _persistentStoreForObjectID: objectID]; 231 232 return [store cacheNodeForObjectID: objectID]; 233 } 234 235 - (NSManagedObject *) objectRegisteredForID: (NSManagedObjectID *) objectID { 236 return NSMapGet(_objectIdToObject, objectID); 237 } 238 239 - (void) _registerObject: (NSManagedObject *) object { 240 [_registeredObjects addObject: object]; 241 NSMapInsert(_objectIdToObject, [object objectID], object); 242 243 NSEntityDescription *entity = [object entity]; 244 NSArray *properties = [[entity propertiesByName] allKeys]; 245 246 for (NSString *key in properties) { 247 [object addObserver: self 248 forKeyPath: key 249 options: NSKeyValueObservingOptionNew | 250 NSKeyValueObservingOptionOld 251 context: nil]; 252 } 253 } 254 255 - (NSManagedObject *) objectWithID: (NSManagedObjectID *) objectID { 256 NSManagedObject *result = NSMapGet(_objectIdToObject, objectID); 257 258 if (result == nil) { 259 result = [[NSManagedObject alloc] initWithObjectID: objectID 260 managedObjectContext: self]; 261 NSMapInsert(_objectIdToObject, objectID, result); 262 [result release]; 263 [self _registerObject: result]; 264 } 265 266 return result; 267 } 268 269 - (NSArray *) executeFetchRequest: (NSFetchRequest *) fetchRequest 270 error: (NSError **) error 271 { 272 NSArray *affectedStores = [fetchRequest affectedStores]; 273 274 if (affectedStores == nil) 275 affectedStores = [_storeCoordinator persistentStores]; 276 277 NSMutableSet *resultSet = [NSMutableSet set]; 278 279 for (NSManagedObject *check in _insertedObjects) { 280 NSEntityDescription *entity = [check entity]; 281 282 if ([entity _isKindOfEntity: [fetchRequest entity]]) { 283 if (![_deletedObjects containsObject: check]) { 284 if ([affectedStores containsObject: [[check objectID] 285 persistentStore]]) { 286 [resultSet addObject: check]; 287 } 288 } 289 } 290 } 291 292 for (NSAtomicStore *store in affectedStores) { 293 NSSet *nodes = [store cacheNodes]; 294 295 for (NSAtomicStoreCacheNode *node in nodes) { 296 NSManagedObjectID *checkID = [node objectID]; 297 NSEntityDescription *entity = [checkID entity]; 298 299 if ([entity _isKindOfEntity: [fetchRequest entity]]) { 300 NSManagedObject *check = [self objectWithID: checkID]; 301 302 if (![_deletedObjects containsObject: check]) { 303 [resultSet addObject: check]; 304 } 305 } 306 } 307 } 308 309 NSMutableArray *result = 310 [NSMutableArray arrayWithArray: [resultSet allObjects]]; 311 312 NSPredicate *p = [fetchRequest predicate]; 313 314 if (p != nil) 315 [result filterUsingPredicate: p]; 316 317 [result sortUsingDescriptors: [fetchRequest sortDescriptors]]; 318 319 return result; 320 } 321 322 - (NSUInteger) countForFetchRequest: (NSFetchRequest *) request 323 error: (NSError **) error 324 { 325 return [[self executeFetchRequest: request error: error] count]; 326 } 327 328 - (void) insertObject: (NSManagedObject *) object { 329 NSPersistentStore *store = 330 [_storeCoordinator _persistentStoreForObject: object]; 331 332 [[object objectID] setStoreIdentifier: [store identifier]]; 333 [[object objectID] setPersistentStore: store]; 334 335 [_insertedObjects addObject: object]; 336 [_updatedObjects addObject: object]; 337 [self _registerObject: object]; 338 } 339 340 - (void) deleteObject: (NSManagedObject *) object { 341 NSArray *properties = [[object entity] properties]; 342 343 for (NSPropertyDescription *property in properties) 344 [object setValue: nil forKey: [property name]]; 345 346 [_deletedObjects addObject: object]; 347 } 348 349 - (void) assignObject: object toPersistentStore: (NSPersistentStore *) store { 350 [object retain]; 351 352 NSMapRemove(_objectIdToObject, [object objectID]); 353 354 [[object objectID] setStoreIdentifier: [store identifier]]; 355 356 [[object objectID] setPersistentStore: store]; 357 358 NSMapInsert(_objectIdToObject, [object objectID], object); 359 [object release]; 360 } 361 362 - (void) detectConflictsForObject: (NSManagedObject *) object { 363 // NOT NEEDED 364 NSUnimplementedMethod(); 365 } 366 367 - (void) refreshObject: (NSManagedObject *) object mergeChanges: (BOOL) flag { 368 // NEEDED 369 NSUnimplementedMethod(); 370 } 371 372 - (void) _requestProcessPendingChanges { 373 if (!_requestedProcessPendingChanges) { 374 375 NSRunLoop *runLoop = [NSRunLoop mainRunLoop]; 376 [runLoop performSelector: @selector(_processPendingChangesForRequest) 377 target: self 378 argument: nil 379 order: 0 380 modes: [NSArray arrayWithObject: 381 NSRunLoopCommonModes]]; 382 _requestedProcessPendingChanges = YES; 383 } 384 } 385 386 - (void) _processPendingChanges { 387 _requestedProcessPendingChanges = NO; 388 } 389 390 - (void) processPendingChanges { 391 if (_requestedProcessPendingChanges) { 392 [[NSRunLoop mainRunLoop] cancelPerformSelector: @selector 393 (_processPendingChangesForRequest) 394 target: self 395 argument: nil]; 396 } 397 398 [self _processPendingChanges]; 399 } 400 401 - (void) _processPendingChangesForRequest { 402 [self _processPendingChanges]; 403 } 404 405 - (void) observeValueForKeyPath: (NSString *) keyPath 406 ofObject: (id) object 407 change: (NSDictionary *) change 408 context: (void *) context 409 { 410 if (NSMapGet(_objectIdToObject, [object objectID]) == object) 411 [_updatedObjects addObject: object]; 412 } 413 414 - (BOOL) obtainPermanentIDsForObjects: (NSArray *) objects 415 error: (NSError **) error 416 { 417 418 for (NSManagedObject *check in objects) { 419 NSManagedObjectID *checkID = [check objectID]; 420 421 if ([checkID isTemporaryID]) { 422 NSAtomicStore *store = (NSAtomicStore *) [checkID persistentStore]; 423 424 NSMapRemove(_objectIdToObject, checkID); 425 426 #if 1 427 if (store == nil) 428 NSLog(@"internal inconsistency , object had no store %@", 429 check); 430 #else 431 if (store == nil) { 432 NSString *storeIdentifier = [checkID storeIdentifier]; 433 434 if (storeIdentifier != nil) 435 store = (NSAtomicStore *) [_storeCoordinator 436 _persistentStoreWithIdentifier: storeIdentifier]; 437 else { 438 store = (NSAtomicStore *) [_storeCoordinator 439 _persistentStoreForObject: check]; 440 [checkID setStoreIdentifier: [store identifier]]; 441 } 442 443 [checkID setPersistentStore: store]; 444 } 445 #endif 446 447 id referenceObject = 448 [store newReferenceObjectForManagedObject: check]; 449 450 [checkID setReferenceObject: referenceObject]; 451 452 [store _uniqueObjectID: checkID]; 453 454 NSMapInsert(_objectIdToObject, checkID, check); 455 } 456 } 457 458 return YES; 459 } 460 461 - (BOOL) save: (NSError **) errorp { 462 NSMutableArray *errors = [NSMutableArray array]; 463 NSMutableArray *errorStores = [NSMutableArray array]; 464 NSError *idError = nil; 465 NSMutableSet *affectedStores = [NSMutableSet set]; 466 467 [[NSNotificationCenter defaultCenter] 468 postNotificationName: NSManagedObjectContextWillSaveNotification 469 object: self]; 470 471 // delete cache nodes 472 for (NSManagedObject *deleted in _deletedObjects) { 473 NSAtomicStore *store = (NSAtomicStore *) [_storeCoordinator 474 _persistentStoreForObject: deleted]; 475 NSAtomicStoreCacheNode *node = 476 [store cacheNodeForObjectID: [deleted objectID]]; 477 478 [store willRemoveCacheNodes: [NSSet setWithObject: node]]; 479 480 [affectedStores addObject: store]; 481 482 // Don't process deleted objects 483 NSMapRemove(_objectIdToObject, [deleted objectID]); 484 485 [_insertedObjects removeObject: deleted]; 486 [_updatedObjects removeObject: deleted]; 487 } 488 489 if (![self obtainPermanentIDsForObjects: [_insertedObjects allObjects] 490 error: &idError]) { 491 NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; 492 493 [userInfo setObject: @"obtainPermanentIDsForObjects failed" 494 forKey: NSLocalizedDescriptionKey]; 495 496 if (errorp != NULL) 497 *errorp = [NSError 498 errorWithDomain: NSCocoaErrorDomain 499 code: NSPersistentStoreIncompleteSaveError 500 userInfo: userInfo]; 501 502 return NO; 503 } 504 505 for (NSManagedObject *inserted in _insertedObjects) { 506 NSAtomicStore *store = (NSAtomicStore *) [_storeCoordinator 507 _persistentStoreForObject: inserted]; 508 NSAtomicStoreCacheNode *node = 509 [store newCacheNodeForManagedObject: inserted]; 510 511 [store addCacheNodes: [NSSet setWithObject: node]]; 512 513 [affectedStores addObject: store]; 514 } 515 516 [_insertedObjects removeAllObjects]; 517 518 for (NSManagedObject *updated in _updatedObjects) { 519 NSAtomicStore *store = (NSAtomicStore *) [_storeCoordinator 520 _persistentStoreForObject: updated]; 521 NSAtomicStoreCacheNode *node = 522 [store cacheNodeForObjectID: [updated objectID]]; 523 524 [store updateCacheNode: node fromManagedObject: updated]; 525 526 [affectedStores addObject: store]; 527 } 528 529 for (NSPersistentStore *store in affectedStores) { 530 NSError *saveError = nil; 531 532 if ([store isKindOfClass: [NSAtomicStore class]]) { 533 NSAtomicStore *atomicStore = (NSAtomicStore *) store; 534 535 if (![atomicStore save: &saveError]) { 536 [errorStores addObject: atomicStore]; 537 [errors addObject: saveError]; 538 } 539 } 540 } 541 542 [[NSNotificationCenter defaultCenter] 543 postNotificationName: NSManagedObjectContextDidSaveNotification 544 object: self]; 545 546 if ([errors count] == 0) 547 return YES; 548 549 NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; 550 551 [userInfo setObject: @"Unable to save managed object context" 552 forKey: NSLocalizedDescriptionKey]; 553 [userInfo setObject: errorStores forKey: NSAffectedStoresErrorKey]; 554 [userInfo setObject: errors forKey: NSDetailedErrorsKey]; 555 556 if (errorp != NULL) 557 *errorp = [NSError errorWithDomain: NSCocoaErrorDomain 558 code: NSPersistentStoreIncompleteSaveError 559 userInfo: userInfo]; 560 561 return NO; 562 } 563 564 - (void) mergeChangesFromContextDidSaveNotification: 565 (NSNotification *) notification 566 { 567 NSUnimplementedMethod(); 568 } 569 570 - (BOOL) commitEditing { 571 NSUnimplementedMethod(); 572 return NO; 573 } 574 575 - (void) commitEditingWithDelegate: (id) delegate 576 didCommitSelector: (SEL) didCommitSelector 577 contextInfo: (void *) contextInfo 578 { 579 NSUnimplementedMethod(); 580 } 581 582 - (void) discardEditing { 583 NSUnimplementedMethod(); 584 } 585 586 - (void) objectDidBeginEditing: (id) editor { 587 NSUnimplementedMethod(); 588 } 589 590 - (void) objectDidEndEditing: (id) editor { 591 NSUnimplementedMethod(); 592 } 593 594 @end