问题
I'm trying to import data into Core Data and save it in a background thread with MagicalRecord.
I'm basically trying to do this:
__block User *user = nil;
[MagicalRecord saveWithBlockAndWait:^(NSManagedObjectContext *localContext)
{
user = [User MR_findFirstOrCreateByAttribute:@"userId" withValue:userId inContext:localContext];
[user MR_importValuesForKeysWithObject:responseObject];
}];
[User setCurrentUser:user];
User is correct when I'm inside the block. After the block is completed user
is a NSManagedObject object, but doesn't have any attributes set.
This, on the other hand, works:
OEUser *user = [OEUser MR_findFirstOrCreateByAttribute:@"userId" withValue:userId];
[user MR_importValuesForKeysWithObject:responseObject];
[[NSManagedObjectContext MR_defaultContext] MR_saveToPersistentStoreAndWait];
[User setCurrentUser:user];
But I want to save it in the background.
I also tried this to no avail.
User *user = [User MR_findFirstOrCreateByAttribute:@"userId" withValue:userId];
[MagicalRecord saveWithBlockAndWait:^(NSManagedObjectContext *localContext)
{
User *localUser = [user MR_inContext:localContext];
[user MR_importValuesForKeysWithObject:responseObject];
}];
[User setCurrentUser:user];
The problem here seems to be that [user MR_inContext:localContext]
returns nil.
I'm pretty new to Core Data, so bear with me if I'm missing something obvious.
回答1:
When you use the *Wait
method you are not gaining any asynchronicity and you will still be blocking your main thread during the import. You should indeed be using the block methods but this to ensure that any Core Data operations are performed on the correct thread (the thread the NSManagedObjectContext
was created on).
When you use saveWithBlockAndWait:
MagicalRecord creates a new context on a background thread to perform it's work on. This newly created context is then yielded to your block and lives only for the scope of the block. You can not pass NSManagedObject
's between contexts so you should not be trying to capture the user from outside the block.
I can imagine the second example will only actually be failing in the case of creating a new User
because you call MR_findFirstOrCreateByAttribute:withValue:
which will create a new NSManagedObject
in memory but this is not persisted to the store therefore inside the saveWithBlockAndWait:
there is no User
to pull from the store.
The correct way would be
[MagicalRecord saveWithBlockAndWait:^(NSManagedObjectContext *localContext) {
User *localUser = [User MR_findFirstOrCreateByAttribute:@"userId"
withValue:userId
inContext:localContext];
[user MR_importValuesForKeysWithObject:responseObject];
}];
User *user = [User MR_findFirstByAttribute:@"userId" withValue:userId];
[User setCurrentUser:user];
As mentioned this will block the main thread so you may want to consider moving to saveWithBlock:completion:
instead which will perform the work in the background and not block the current thread.
Even better I would consider not holding onto an actual User
object but instead hold onto the userId
, which will safe you headaches down the line when people start accessing the currentUser
from all kinds of threads
来源:https://stackoverflow.com/questions/23768124/magicalrecord-how-to-save-imported-data-in-background