8
votes

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*/
2
Could you describe a little about the process you use to (a) create this User, and then (b) access it, where you see a nil id? That's a temporary object ID, so it looks like you're using a managed object ID from before the object was saved.Tom Harrington
@TomHarrington: Edited the original question with the info you asked!Trunal Bhanse
What is bgContext, how is it created? is it for a background thread?Wain
@Wain: edited. thanks!Trunal Bhanse
You don't actually appear to be using threads here, and you aren't using performBlock:.Wain

2 Answers

11
votes

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.)

1
votes

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.