NSManagedObjectContext async import, save and notify main context - iphone

I have got parent child context as follow:
1. writercontext with NSPrivateQueueConcurrencyType
2. mainContext with NSMainQueueConcurrencyType ParentContext:writercontext
3. and background context with NSPrivateQueueConcurrencyType ParentContext:writercontext
how can i notify the main context with changes made by background context?
i have read the last part: async save but that doesnt save or import in the background and it gets the UI blocked and unresponsive. is there a way with child parent context in backgound and still notify main context?
currently i save my context:
[context performBlockAndWait:^{
#try {
NSError *childError = nil;
if ([context save:&childError])
{
[context.parentContext performBlockAndWait:^{
NSError *parentError = nil;
if ([context.parentContext save:&parentError])
{
//saved
}
else
{
nslog(#"Error: %#", parentError.description);
}
}];
}
else
{
DBERROR(#"Error: %#", childError.description);
}
}
#catch (NSException *exception)
{
DBERROR(#"Exception: %#", exception.description);
}
}];

I assume that context is your background context.
If you call performBlockAndWait from the main thread, it will be blocked until the block completes. You should replace your code with:
[context performBlock:^{
...
}];
That way the main thread won't be blocked, because the block will be executed on another thread.
As for saving, I guess that your changes don't propagate to your mainContext. I haven't used nested contexts myself, so I'm not sure why it's working this way (maybe you need to merge changes between contexts manually).

Related

CoreData thread safe object context save [duplicate]

The following method gets called in order to populate my Core-Data after AFNetworking fetches information from my app server.
The information seems to be perfectly working as when the table is updated I can see the new information being updated in the UITableView.
Now the problem that I have is that even tho I can see the information ( after it has been fetches from the server, stored into Core-data and refetches to display in my UITableView) If I then go and close my app and re open it, the information is not there anymore.
It seems as if the information is not persistent and the problem seems to be the thread. given that if I remove the thread option in my method everything works fine.
What am I missing?? I have tried most things that I came across but I can't seem to find a solution.
NSManagedObjectContext *childContext = [[NSManagedObjectContext alloc]
initWithConcurrencyType:NSPrivateQueueConcurrencyType];
childContext.parentContext = managedObjectContext;
myModel.context = childContext;
[childContext performBlock:^{
// ... Lots Controller logic code that then calls the class myModel where all my Core-Data save methods are
// Sort Wall Pictures
if ( [dataHolder[#"verb"] isEqualToString:#"addWallPicture"] ) {
data = #{ #"resourceID":dataHolder[#"_id"][#"$id"],
#"resourceName":dataHolder[#"details"][#"resourceName"],
#"author":#{ #"id":dataHolder[#"userId"][#"$id"],
#"username":dataHolder[#"details"][#"authorName"] },
#"likesNumber":#0,
#"likesPeople":#[]
};
[myModel saveSocialWall:data date:date verb:dataHolder[#"verb"] code:dataHolder[#"_id"][#"$id"] myUser:myUser];
continue;
}
[childContext save:&error];
}];
You have to save the main context as well at some point, e.g. after saving the child context.
Saving the child context saves only to the main context, and saving the main context saves to the store file.
Like this (written on the phone, there will
be syntax errors):
// ...
[childContext save:&error];
[mainContext performBlock:^{
[mainContext save:&error];
}];
In Swift 2.0 that would be:
do {
try childContext.save()
mainContext.performBlock {
do {
try mainContext.save()
} catch let err as NSError {
print("Could not save main context: \(err.localizedDescription)")
}
}
} catch let err as NSError {
print("Could not save private context: \(err.localizedDescription)")
}

Wait for URLConnection block to finish

I'm creating a REST client class for my iPad app. So I created a BOOL method which does the login using an NSURLConnection subclass I created earlier.
This JWURLConnection has block type properties for the finishLoading and failWithError operations.
The Problem is that the URL connection most likely finishes (or fails) AFTER this method is completely executed. A cannot use an extra method to use performSelector:waitUntilDone: too because I have to wait for the connection.
Now I tried using plain C semaphores and an extra thread (so that the semaphore blocks only the RESTClient thread, not the URLConnections one), but I had no success; the method started waiting but the whole connection stuff was frozen, thus there where no NSLogs from the connection.
The JWURLConnection starts it's own thread by itself within the -start method:
- (void)start { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [super start]; }); }
Here is the code I tried it with (using semaphores):
- (BOOL)loginWithUsername:(NSString *)uName ansPassword:(NSString *)pWord {
__block BOOL loginSucceeded = NO;
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
JWURLConnection *connection = [JWURLConnection connectionWithPOSTRequestToURL:POSTData:];
[connection setFinished^(NSData *data) {
// validate server response and set login variable
loginSucceeded = YES;
dispatch_semaphore_signal(sema);
}];
[connection setFailed:^(NSError *error) {
loginSucceeded = NO;
NSLog(#"Login failed: %#", [error description]);
dispatch_semaphore_signal(sema);
}];
[connection start];
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
// do some more stuff like error handling / reporting here
return loginSucceeded;
}
I hope you can lead my the right direction...
The JWURLConnection starts it's own thread by itself within the -start method:
- (void)start { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [super start]; }); }
You need to ensure that a NSURLConnection's delegate methods will be scheduled on a NSRunLoop or a NSOperationQueue. While the start method could actually take care of this - the given code and your comment indicate it does not ;) In short, dispatch_async does not guarantee that the underlaying thread has a run loop and a dispatch queue does not even guarantee that the underlaying thread is always the same.
The docs show how to schedule a connection.
I would suggest to schedule the connection on the main thread, and change this to a NSOperationQueue when required.
Your loginWithUsername:andPassword: method will simply return immediately since you call/invoke an asynchronous function/method.
Employing asynchronous patterns is kinda "infectious". Once you started using asynchronous programming style, you cant get "rid of" it unless you use synchronization primitives that block the current thread. I would suggest to keep the async style:
- (void) loginWithUsername:(NSString *)uName
andPassword:(NSString *)pWord
completion:(void(^)(id result))onCompletion;
And later:
[self loginWithUsername:#"Me" andPassword:#"secret" completion:^(id result) {
if ([result != [isKindOfError class]]) {
[self fetchImagesWithURL:url completion: ^(id result) {
...
}];
}
}];

saving NSManagedObjectContext causes the app to wait infinitely

Why is it that everytime I call save on my NSManagedObjectContext:
-(NSManagedObjectContext*)managedObjectContext {
NSMutableDictionary* threadDictionary = [[NSThread currentThread] threadDictionary];
NSManagedObjectContext* backgroundThreadContext = [threadDictionary objectForKey:RKManagedObjectStoreThreadDictionaryContextKey];
if (!backgroundThreadContext) {
backgroundThreadContext = [self newManagedObjectContext];
[threadDictionary setObject:backgroundThreadContext forKey:RKManagedObjectStoreThreadDictionaryContextKey];
[backgroundThreadContext release];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(mergeChanges:)
name:NSManagedObjectContextDidSaveNotification
object:backgroundThreadContext];
}
return backgroundThreadContext;
}
- (NSError*)save {
NSManagedObjectContext* moc = [self managedObjectContext];
NSError *error = nil;
#try {
if (![moc save:&error]) { //breakpoint in here
//some code
}
the app seems to be waiting forever and never got back to resume it's execution? Here's what I mean in a video. Can this be possibly caused because something is wrong with the entity/relationship model?
Here's a screenshot of the leaks instruments, I don't see any leaks, but it seems that the app is allocating something that builds up:
Have you tried ditching your multi-threading code to see if it works? My guess would be that you're mixing up threads here and accessing/saving the MOC from different threads. Managing threads manually is a PITA, you should try switching to Grand Central Dispatch.
I would also give your main MOC its own accessor so you can make sure it isn't called from background threads, and have some: - (NSManagedObjectContext*)newBackgroundMOC; and - (void)saveBackgroundMOC:(NSManagedObjectContext*)context; methods to quickly create and save MOCs from background queues/threads:
dispatch_async(my_queue, ^{
NSManagedObjectContext *context = [self newBackgroundMOC]; // create context, setup didSave notification to merge with main MOC, etc
// modify context
[self saveBackgroundMOC:context]; // main MOC gets updated
});
Migrating to GCD is a bit of work, but in the long run you'll see it's much more pleasant to work with. It goes without saying that it's also the most modern and recommended by Apple way to deal with threads.

Core Data concurrency - NSManagedObjectContext

So I need to use cora-data managed object context using multiple thread.
I found the below implementation to be pretty much what I need.
I have 2 question :
Where do I release the MOC that I allocate ? I don't want to go through every function thread in my app and to call a function to release the MOC.
I'd rather write the function, and somehow make it so it be called by every thread prior to his exit. is that possible ?
Is the below implementation enough for core data concurrency ?
-(void) StoreManagedObjectContextForCurrentThread:(NSManagedObjectContext*) context
{
[[[NSThread currentThread] threadDictionary] setObject: context forKey: AQPerThreadManagedObjectContext];
}
-(NSManagedObjectContext *) myManagedObjectContext {
NSManagedObjectContext * result = [[[NSThread currentThread] threadDictionary] objectForKey: AQPerThreadManagedObjectContext];
if ( result != nil )
return result;
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator!=nil)
{
NSManagedObjectContext * moc = [[NSManagedObjectContext alloc] init];
[moc setMergePolicy: NSMergeByPropertyObjectTrumpMergePolicy];
[moc setPersistentStoreCoordinator: coordinator];
[self StoreManagedObjectContextForCurrentThread:moc];
[moc release]; // now owned by the thread dictionary
return moc;
}
return nil;
}
In the implementation you've provided, you haven't got a way to release the managed object context before the thread disappears, because its lifetime is dictated by the thread dictionary, and that object's lifetime is handled by Foundation, not by you.
If you want to manage the objects' lifetimes, you need to manage their storage. That means moving away from using -threadDictionary. One option is to implement your background work as NSOperation subclasses and keep the managed object contexts around for the lifetime of the operations, but that's likely a big change from what you currently have.

Cocoa thread synchronisation when using [ALAssetsLibrary enumerateGroupsWithTypes:]

I have recently, like a few people, discovered that [ALAssetsLibrary enumerateGroupsWithTypes] likes to run its blocks on another thread. What a shame that Apple didn't document that :-)
In my current circumstance I need to wait for the enumeration to complete, before the main thread returns any results. I clearly need some sort of thread synchronisation.
I've read about NSLock & NSConditionLock, but nothing yet seems to fit the requirement of 'signal a blocked thread that this worker thread has completed'. It seems like a simple enough need - can anyone point me in the right direction?
Your clue & boos, are most welcome as always,
M.
The framework doesn't run these blocks on a separate thread. It just runs them as additional events in the same run-loop. To prove it, try this
[library enumerateGroupsWithTypes:ALAssetsGroupAll
usingBlock:[^(ALAssetsGroup * group, BOOL * stop)
{
if([NSThread isMainThread])
{
NSLog(#"main");
}
else
{
NSLog(#"non-main");
}
} copy]
failureBlock:^(NSError * err)
{NSLog(#"Erorr: %#", [err localizedDescription] );}];
[library release];
if([NSThread isMainThread])
{
NSLog(#"main");
}
else
{
NSLog(#"non-main");
}
My output from this was
main
main
main
Meaning that the block was being called in the main thread. It's just a separate event.
To solve your problem, you just need to return your value somehow from within the block when you reach the last step. You can tell it's the last step because your block will be called with nil for the group object.
EDIT: for instance use this block
^(ALAssetsGroup * group, BOOL * stop)
{
if(group == nil)
{
// we've enumerated all the groups
// do something to return a value somehow (maybe send a selector to a delegate)
}
}
The answer is to use the NSConditionLock class thusly ...
typedef enum {
completed = 0,
running = 1
} threadState;
...
NSConditionLock *lock = [[NSConditionLock alloc] initWithCondition:running];
Then spin off your thread, or in my case a call to [ALAssetsLibrary enumerateGroupsWithTypes:]. Then block the parent thread with this ...
// Await completion of the worker threads
[lock lockWhenCondition:completed];
[lock unlockWithCondition:completed];
When all work is done in the child/worker thread, unblock the parent with this ...
// Signal the waiting thread
[lock lockWhenCondition:running];
[lock unlockWithCondition:completed];
Simply use this:
[library enumerateGroupsWithTypes:ALAssetsGroupAll
usingBlock:[^(ALAssetsGroup * group, BOOL * stop)
{
if(group == nil)
{
// this is end of enumeration
}
}
.
.
.