Trunal Bhanse
Trunal Bhanse

Reputation: 1691

Core Data not firing fault for an NSManagedObject instance as property on app delegate

I import the logged in user's data from server into a Core Data Entity called "User". I also keep a reference of this specific User object onto my AppDelegate (as a property) so I can access it elsewhere in my app. The problem I am facing is, when I push another view controller and try to access appdelegate.loggedInUser.id , I see that "id" is nil. Debugger shows this for the object :

$24 = 0x0b28ad30 <User: 0xb28ad30> (entity: User; id: 0xb261160 <x-coredata:///User/tC48E8991-B8A6-4E68-9112-93F9F21DB5382> ; data: <fault>)

My understanding was that the Core Data framework would fire the fault the moment I try to access one of the properties of this object. I am confused as to why me accessing the "id" property of the user is not firing a fault in this case?

EDIT:

This is how create and use the loggedInUser object :

//method to get bgContext 
+(NSManagedObjectContext *)getContextOnBgWithParentSetToMainMOC
{
  NSManagedObjectContext *tmpContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
  [tmpContext setParentContext:[Utils getAppDelegate].managedObjectContext];
  return tmpContext;
}

//in App Delegate
NSManagedObjectContext *bgContext = [NSManagedObjectContext getContextOnBgWithParentSetToMainMOC];
   self.loggedInUser = [User importFromObject:loggedInUserData inContext:bgContext completionBlock:^(NSManagedObjectContext *theContext, NSManagedObject *theManagedObjectWithValuesImported) {}];

//In User.m file
+ (User *)importFromObject:(NSDictionary *)dictionary inContext:(NSManagedObjectContext *)context completionBlock:(TemporaryContextImportReturnBlock)block {

  if ( !context ){
    context = [NSManagedObjectContext getContextOnBgWithParentSetToMainMOC];
  }

  NSManagedObjectContext *localContext = context;
    User *newUserEntity = [NSEntityDescription insertNewObjectForEntityForName:@"User" inManagedObjectContext:localContext];
    NSArray *emailsArray = [dictionary objectForKey:@"emails"];
    NSString *emailsString = @"";
    if ([emailsArray count] > 0){
      emailsString = [emailsArray componentsJoinedByString:@","];
    }
    newUserEntity.emails = emailsString;
    newUserEntity.id = [dictionary objectForKey:@"id"];
    newUserEntity.n = [dictionary nonNullObjectForKey:@"n"];
  return newUserEntity;
}

//Access in one of the view controllers
    User *loggedInUser = [Utils getAppDelegate].loggedInUser;
//    loggedInUser.id /*nil*/

Upvotes: 8

Views: 2337

Answers (2)

Vil&#233;m Kurz
Vil&#233;m Kurz

Reputation: 3410

make sure, that you do not call

[managedObjectContext reset];

somewhere. From Apple doc:

Returns the receiver to its base state. All the receiver's managed objects are “forgotten.” If you use this method, you should ensure that you also discard references to any managed objects fetched using the receiver, since they will be invalid afterwards.

Those "orphaned" managed object's managedObjectContext property will change to nil and they will not be able to fire faults anymore.

Upvotes: 2

jsadler
jsadler

Reputation: 619

I have the same problem. It turns out, according to this answer, which references the Apple docs, that an NSManagedObject does not hold a strong reference to its NSManagedObjectContext as you might expect. I suspect that if you inspect your object when it doesn't fire the fault properly that [myObject managedObjectContext] == nil.

I don't know what best practices are here. The obvious (but potentially difficult) solution is to find out why your MOC is being deallocated. As an alternative, although I'm unsure whether it's safe to do, you could retain the MOC from each NSManagedObject instance. (I have question about that open here.)

Upvotes: 13

Related Questions