Little confusion about Threading and DataConsistent on Realm - objective-c

You can have any number of threads working on the same Realms in parallel, and since they all have their own snapshots, they will never cause each other to see inconsistent state
Consider if the write thread change some value and complete faster than the read thread, however, since the read thread has its own snapshots, it won't change because what have done on the write thread, but if the read thread's operation is base on the latest value, so it's that means the read thread can not do the right operation? thx for helping.
my test demo to describe my confusion

By "inconsistent", the documentation mean "partially modified". They cannot be partially modified, because you see specific version that was created by transactional changes, at all times.
However, updating is not immediate across threads.
since the read thread has its own snapshots, it won't change because what have done on the write thread, but if the read thread's operation is base on the latest value, so it's that means the read thread can not do the right operation?
True, that is exactly why the read thread should use collection notifications.
// from https://realm.io/docs/objc/latest/#collection-notifications
- (void)viewDidLoad {
[super viewDidLoad];
// Observe RLMResults Notifications
__weak typeof(self) weakSelf = self;
self.notificationToken = [[Person objectsWhere:#"age > 5"] addNotificationBlock:^(RLMResults<Person *> *results, RLMCollectionChange *changes, NSError *error) {
if (error) {
NSLog(#"Failed to open Realm on background worker: %#", error);
return;
}
UITableView *tableView = weakSelf.tableView;
// Initial run of the query will pass nil for the change information
if (!changes) {
[tableView reloadData];
return;
}
// Query results have changed, so apply them to the UITableView
[tableView beginUpdates];
[tableView deleteRowsAtIndexPaths:[changes deletionsInSection:0]
withRowAnimation:UITableViewRowAnimationAutomatic];
[tableView insertRowsAtIndexPaths:[changes insertionsInSection:0]
withRowAnimation:UITableViewRowAnimationAutomatic];
[tableView reloadRowsAtIndexPaths:[changes modificationsInSection:0]
withRowAnimation:UITableViewRowAnimationAutomatic];
[tableView endUpdates];
}];
}
- (void)dealloc {
[self.notificationToken stop];
}
In addition to that, transactions always "create the latest version" (and there can be only 1 transaction at a time), so a query inside a write transaction cannot be out-of-date.
dispatch_async(writeQueue, ^{
[[RLMRealm defaultRealm]] transactionWithBlock:^{
Cinema* b = [Cinema allObjects].firstObject; // query cannot be out of date
...
Obviously this does NOT mean that you should execute transaction on the UI thread just to bump its version. Use the notification token for that.

Yes, this is correct. If you want to make sure that you always get consistent data form Realm, do not access it concurrently from separate threads, since the write transactions done on one thread will not be reflected on the other thread. This is the behaviour that can be seen in your example as well.
There is also no need to access realm asynchronously unless you have a huge number of objects to read from it/write to it. If you want to work with such a big number of objects that your queries/write transactions would take several seconds and hence doing them synchronously would block UI updates for too long, make sure that you only access Realm from a single thread and also make sure that your asynchronous read/write operations are not running in parallel.

Related

NSOperationQueue notification

Help me out here or just shed some light on the problem.
I have a scenario where I perform a sync of archived messages on a openfire server and I handle and store all incoming messages with NSOperations and NSOperationQueue.
I want to get notified when the NSOperationQueue is done, but I can't simply count the number of operations it has running. At times the NSOperationQueue has 0 operations because it depends on data to arrive form the server.
The NSOperations start methods
- (void)startArchiveSyncStore:(XMPPIQ *)iq operationID:(NSString *)xmlID {
#autoreleasepool {
if (![self.pendingOperations.archiveStoreInProgress.allKeys containsObject:xmlID]) {
ArchiveStoreOperation *storeOperation = [[ArchiveStoreOperation alloc] initWithMessagesToArchive:iq withID:xmlID delegate:self];
[self.pendingOperations.archiveStoreInProgress setObject:storeOperation forKey:xmlID];
[self.pendingOperations.archiveStoreQueue addOperation:storeOperation];
}
}
}
- (void)startArchiveSycnDownload:(XMPPIQ *)iq operationID:(NSString *)xmlID {
#autoreleasepool {
if (![self.pendingOperations.archiveDownloadInProgress.allKeys containsObject:xmlID]) {
ArchiveDownloadOperation *downloadOperation = [[ArchiveDownloadOperation alloc] initWithMessagesToDownload:iq withID:xmlID delegate:self];
[self.pendingOperations.archiveDownloadInProgress setObject:downloadOperation forKey:xmlID];
[self.pendingOperations.archiveDownloadQueue addOperation:downloadOperation];
}
}
}
And this is the main thread callback performed by the NSOperation:
- (void)archiveStoreDidFinish:(ArchiveStoreOperation *)downloader {
NSString *xmlID = downloader.xmlnsID;
DDLogInfo(#"%# %#", THIS_METHOD, xmlID);
[self.pendingOperations.archiveStoreInProgress removeObjectForKey:xmlID];
}
These operations start when I receive iq stanzas containing lists of the chat history from the openfire server. Then I handle these lists like so:
- (BOOL)xmppStream:(XMPPStream *)sender didReceiveIQ:(XMPPIQ *)iq {
if ([iq isResultIQ]) {
if ([iq elementForName:#"list" xmlns:#"urn:xmpp:archive"]) {
[self startArchiveSycnDownload:iq operationID:[[iq attributeForName:#"id"] stringValue]];
}
if ([iq elementForName:#"chat" xmlns:#"urn:xmpp:archive"]) {
[self startArchiveSyncStore:iq operationID:[[iq attributeForName:#"id"] stringValue]];
}
}
return NO;
}
Any ideas folks ? Thanks in advance...
From my understanding each NSOperation has an isFinished property that you can check for. But, there is a caveat - isFinished doesn't guarantee that the operation has completed successfully. It is set to true if it succeeds but also if it has been cancelled or an error has occurred.
Obviously each queue has a count of the operations [queue.operations count] but as you've said that won't be of use here.
One alternative is to use KVO. You could try setting this up between the other object that you're using and the NSOperationQueue. You could add an observer to the queue and check that no other operations are in effect.
Also, check this SO post here if you haven't already.
I use NSNotificationCenter and post whenever a NSOperation in the last queue finishes. I assume that there is a "last" queue, aka the one that gets spun up after other queue operations have finished.
When you receive the notification check the count of all your NSOperationQueues to see if they are empty.
It's not clear from your question exactly what condition you do consider to be "done" (no operations in the queue and… what?).
One approach is to create a completion operation. As you create the other operations, add each as a dependency of the completion operation. The completion operation can be in some other queue, possibly [NSOperationQueue mainQueue]. When there are no other operations outstanding, the completion operation will execute.
If you have some other condition than other operations outstanding that means the queue is not "done", then you need to explain. If it's that network downloading is in progress, then maybe you need to wrap such downloading in an operation.
You could also use a custom subclass of NSOperation for the completion operation and override -isReady to use whatever criteria it wants to augment the superclass's notion of readiness. Of course, if you do that, you need to generate KVO change notifications when those other criteria change.

Barrier operations in NSOperationQueue

How can we implement dispatch_barrier_async's equivalent behavior using NSOperationQueue or any user-defined data-structure based on NSOperationQueue?
The requirement is, whenever a barrier operation is submitted it should wait until all non-barrier operations submitted earlier finish their execution and blocks other operations submitted after that.
Non-barrier operations should be able to perform concurrently.
Barrier operations should execute serially.
NB: Not using GCD,as it doesn't provide(or atleast difficult) much access over the operations, like cancelling single operation, etc.
This is more or less what jeffamaphone was saying, but I put up a gist that should, in rough outline, do what you ask.
I create a NSMutableArray of NSOperationQueues, which serves as a "queue of queues". Every time you add a BarrierOperation object, you tack a fresh suspended op queue on the end. That becomes the addingQueue, to which you add subsequent operations.
- (void)addOperation:(NSOperation *)op {
#synchronized (self) {
if ([op isKindOfClass:[BarrierOperation class]]) {
[self addBarrierOperation:(id)op];
} else {
[[self addingQueue] addOperation:op];
}
}
}
// call only from #synchronized block in -addOperation:
- (void)addBarrierOperation:(BarrierOperation *)barrierOp {
[[self addingQueue] setSuspended:YES];
for (NSOperation *op in [[self addingQueue] operations]) {
[barrierOp addDependency:op];
}
[[self addingQueue] addOperation:barrierOp];
// if you are free to set barrierOp.completionBlock, you could skip popCallback and do that
__block typeof(self) weakSelf = self;
NSOperation *popCallback = [NSBlockOperation blockOperationWithBlock:^{
[weakSelf popQueue];
}];
[popCallback addDependency:barrierOp];
[[self addingQueue] addOperation:popCallback];
[[self addingQueue] setSuspended:NO];
NSOperationQueue *opQueue = [[NSOperationQueue alloc] init];
[opQueue setSuspended:YES];
[_queueOfQueues addObject:opQueue]; // fresh empty queue to add to
}
When one NSOperationQueue finishes, it gets popped and the next one starts running.
- (void)popQueue
{
#synchronized (self) {
NSAssert([_queueOfQueues count], #"should always be one to pop");
[_queueOfQueues removeObjectAtIndex:0];
if ([_queueOfQueues count]) {
// first queue is always running, all others suspended
[(NSOperationQueue *)_queueOfQueues[0] setSuspended:NO];
}
}
}
I might have missed something crucial. The devil's in the details.
This smells a bit like a homework assignment to me. If so, tell me what grade I get. :)
Addendum: Via abhilash1912's comment, a different but similar approach. That code is tested, so it already wins. But it is a bit stale (2 years or so as of today; some deprecated method usage). Moreover, I question whether inheriting from NSOperationQueue is the best path, though it has the virtue of retaining familiarity. Regardless, if you've read this far, it's probably worth looking over.
If you create or find the world's greatest BarrierQueue class, please let us know in the comments or otherwise, so it can be linked up.
Create an NSOperation that is your barrier, then use:
- (void)addDependency:(NSOperation *)operation
To make that barrier operation dependent on all the ones you want to come before it.
I don't think it's possible to create an NSOperation object that's gives you the same sort of functionality, barriers have more to do with the way the queue operates.
The main difference between using a barrier and the dependency mechanism of NSOperations is, in the case of a barrier, the thread queue waits until all running concurrent operations have completed, and then it runs your barrier block, while making sure that any new blocks submitted and any blocks waiting do not run until the critical block has passed.
With an NSOperationQueue, it's impossible to set up the queue in such a way that it'll enforce a proper barrier: all NSOperations added to the queue before your critical NSOperation must be explicitly registered as a dependency with the critical job, and once the critical job has started, you must explicitly guard the NSOperationQueue to make sure no other clients push jobs onto it before the critical job has finished; you guard the queue by adding the critical job as a dependency for the subsequent operations.
(In the case where you know there's only one critical job at a time, this sounds sorta easy, but there will probably be n critical jobs waiting at any one time, which means keeping track of the order jobs are submitted, managing the relative dependency of critical jobs relative to their dependent jobs -- some critical jobs can wait for others, some must be executed in a particular order relative to others... yikes.)
It might be possible to get this level of functionality by setting up an NSOperationQueue with a concurrent job max of one, but that sorta defeats the purpose of doing this, I think. You might also be able to make this work by wrapping an NSOperationQueue in a facade object that protects NSOperations that are submitted "critically."
Just another way... don't hurt me.
Todo: save origin completion and self.maxConcurrentOperationCount = 1 sets queue to serial on adding. But should before execution.
#import "NSOperationQueue+BarrierOperation.h"
#implementation NSOperationQueue (BarrierOperation)
- (void)addOperationAsBarrier:(NSOperation *)op
{
//TODO: needs to save origin completion
// if (op.completionBlock)
// {
// originBlock = op.completionBlock;
// }
NSOperationQueue* qInternal = [NSOperationQueue new];
NSInteger oldMaxConcurrentOperationCount = self.maxConcurrentOperationCount;
op.completionBlock = ^{
self.maxConcurrentOperationCount = oldMaxConcurrentOperationCount;
NSLog(#"addOperationAsBarrier maxConcurrentOperationCount restored");
};
[self addOperationWithBlock:^{
self.maxConcurrentOperationCount = 1;
NSLog(#"addOperationAsBarrier maxConcurrentOperationCount = 1");
}];
[qInternal addOperationWithBlock:^{
NSLog(#"waitUntilAllOperationsAreFinished...");
[self waitUntilAllOperationsAreFinished];
}];
NSLog(#"added OperationAsBarrier");
[self addOperation:op];
}
#end

ios coredata updates not seen on different managed object contexts - data is different between contexts

We're having this issue where different threads see different data on the same records but with different managed object contexts (moc). Our app syncs in the background to a server API. All of the syncing is done on it's own thread and using it's own moc. However, we've discovered that when data gets updated on the main moc that change in data is not shown in the background moc. Any ideas what could be happening? Here's some more details: we're using grand central dispatch like so to put the sync operations on it's own thread: We've checked which queue things are running on and it all is happening on the queue expected.
- (void) executeSync; {
dispatch_async(backgroundQueue, ^(void) {
if([self isDebug])
NSLog(#"ICSyncController: executeSync queue:%# \n\n\n\n\n", [self queue]);
for(id <ICSyncControllerDelegate> delegate in delegates){
[delegate syncController:self];
}
if([ICAccountController sharedInstance].isLoggedIn == YES && shouldBeSyncing == YES) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 300ull * NSEC_PER_SEC), dispatch_get_current_queue(), ^{
[self executeSync];
});
}
});
}
here's how we create the background moc and we've confirmed that it's created on the background queue.
- (NSManagedObjectContext*)backgroundObjectContext {
if (_backgroundObjectContext)
return _backgroundObjectContext;
_backgroundObjectContext = [[NSManagedObjectContext alloc] init];
[_backgroundObjectContext setPersistentStoreCoordinator:self.persistentStoreCoordinator];
[_backgroundObjectContext setStalenessInterval:0.0];
return _backgroundObjectContext;
}
I should add that our background moc is requerying for data and those records returned from that action still have the old values for some fields. How does the background moc get the current data that was already saved by the main moc? I thought just by requerying I would get the current state of these records..
by requerying I mean the following:
The background MOC is executing another "query" to get "fresh" data after the records have been changed by the main moc, yet the data has old values - not the updated values seen in the main moc.
+ (NSArray *)dirtyObjectsInContext:(NSManagedObjectContext *)moc {
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SUBQUERY(memberships, $m, $m.category.name == %# AND $m.syncStatus > %d).#count > 0", MANAGED_CATEGORY_FAVORITES, ManagedObjectSynced];
return [self managedObjectsWithPredicate:predicate inContext:moc];
}
Your help is hugely appreciated as we've been trying to figure this out, or find a work around that doesn't include ditching our threads for days now.
That's how it's supposed to work -- indeed, an important role of the managed object context is to protect you from changes to the data made in other threads. Imagine the havoc that would result if you had a background thread modifying the same objects that the main thread was using without some sort of synchronization scheme.
Read Communicating Changes Between Contexts to learn how to merge changes from one context into another.
I use the following code to listen for changes on context 2, so that context 1 keeps up to date:
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver:context1
selector:#selector(contextTwoUpdated:)
name:NSManagedObjectContextDidSaveNotification
object:context2];
it causes this method to be called on context 1, and i invoke the merge method:
- (void)contextTwoUpdated:(NSNotification *)notification {
[context1 mergeChangesFromContextDidSaveNotification:notification];
}
a side effect of this is any NSFetchedResultsController that is attached to context1 will send a variety of messages to its delegate informing it of the changes,
i've never tried listening both ways while the user changes the object and you update them on the user from behind - i suspect you may have to manage merges if that's the case, since it's one-way (and all user driven) for me i assume all updates to be valid

NSFetchedResultsController not displaying changes from background thread

My app is using an NSFetchedResultsController tied to a Core Data store and it has worked well so far, but I am now trying to make the update code asynchronous and I am having issues. I have created an NSOperation sub-class to do my updates in and am successfully adding this new object to an NSOperationQueue. The updates code is executing as I expect it to and I have verified this through debug logs and by examining the SQLite store after it runs.
The problem is that after my background operation completes, the new (or updated) items do not appear in my UITableView. Based on my limited understanding, I believe that I need to notify the main managedObjectContext that changes have occurred so that they may be merged in. My notification is firing, nut no new items appear in the tableview. If I stop the app and restart it, the objects appear in the tableview, leading me to believe that they are being inserted to the core data store successfully but are not being merged into the managedObjectContext being used on the main thread.
I have included a sample of my operation's init, main and notification methods. Am I missing something important or maybe going about this in the wrong way? Any help would be greatly appreciated.
- (id)initWithDelegate:(AppDelegate *)theDelegate
{
if (!(self = [super init])) return nil;
delegate = theDelegate;
return self;
}
- (void)main
{
[self setUpdateContext:[self managedObjectContext]];
NSManagedObjectContext *mainMOC = [self newContextToMainStore];
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver:self
selector:#selector(contextDidSave:)
name:NSManagedObjectContextDidSaveNotification
object:updateContext];
[self setMainContext:mainMOC];
// Create/update objects with mainContext.
NSError *error = nil;
if (![[self mainContext] save:&error]) {
DLog(#"Error saving event to CoreData store");
}
DLog(#"Core Data context saved");
}
- (void)contextDidSave:(NSNotification*)notification
{
DLog(#"Notification fired.");
SEL selector = #selector(mergeChangesFromContextDidSaveNotification:);
[[delegate managedObjectContext] performSelectorOnMainThread:selector
withObject:notification
waitUntilDone:YES];
}
While debugging, I examined the notification object that is being sent in contextDidSave: and it seems to contain all of the items that were added (excerpt below). This continues to make me think that the inserts/updates are happening correctly but somehow the merge is not being fired.
NSConcreteNotification 0x6b7b0b0 {name = NSManagingContextDidSaveChangesNotification; object = <NSManagedObjectContext: 0x5e8ab30>; userInfo = {
inserted = "{(\n <GCTeam: 0x6b77290> (entity: GCTeam; id: 0xdc5ea10 <x-coredata://F4091BAE-4B47-4F3A-A008-B6A35D7AB196/GCTeam/p1> ; data: {\n changed =
The method that receives your notification must indeed notify your context, you can try something like this, which is what I am doing in my application:
- (void)updateTable:(NSNotification *)saveNotification
{
if (fetchedResultsController == nil)
{
NSError *error;
if (![[self fetchedResultsController] performFetch:&error]) {
//Update to handle the error appropriately.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
exit(-1); // Fail
}
}
else
{
NSManagedObjectContext *context = [fetchedResultsController managedObjectContext];
// Merging changes causes the fetched results controller to update its results
[context mergeChangesFromContextDidSaveNotification:saveNotification];
// Reload your table view data
[self.tableView reloadData];
}
}
Hope that helps.
Depending on the specifics of what you are doing, you may be going about this the wrong way.
For most cases, you can simply assign a delegate using NSFetchedResultsControllerDelegate. You provide an implementation for one of the methods specified in "respondingToChanges" depending on your needs, and then send the tableView a reloadData message.
The answer turned out to be unrelated to the posted code which ended up working as I expected. For reasons that I am still not entirely sure of, it had something to do with the first launch of the app. When I attempted to run my update operation on launches after the Core Data store was created, it worked as expected. I solved the problem by pre-loading a version of the sqlite database in the app so that it did not need to create an empty store on first launch. I wish I understood why this solved the problem, but I was planning on doing this either way. I am leaving this here in the hope that someone else may find it useful and not lose as much time as I did on this.
I've been running into a similar problem in the simulator. I was kicking off an update process when transitioning from the root table to the selected folder. The update process would update CoreData from a web server, save, then merge, but the data didn't show up. If I browsed back and forth a couple times it would show up eventually, and once it worked like clockwork (but I was never able to get that perfect run repeated). This gave me the idea that maybe it's a thread/event timing issue in the simulator, where the table is refreshing too fast or notifications just aren't being queued right or something along those lines. I decided to try running in Instruments to see if I could pinpoint the problem (all CoreData, CPU Monitor, Leaks, Allocations, Thread States, Dispatch, and a couple others). Every time I've done a "first run" with a blank slate since then it has worked perfectly. Maybe Instruments is slowing it down just enough?
Ultimately I need to test on the device to get an accurate test, and if the problem persists I will try your solution in the accepted answer (to create a base sql-lite db to load from).

Async call in Objective-C

I'm trying to get data from a website- xml. Everything works fine.
But the UIButton remains pressed until the xml data is returned and thus if theres a problem with the internet service, it can't be corrected and the app is virtually unusable.
here are the calls:
{
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
if(!appDelegate.XMLdataArray.count > 0){
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
[appDelegate GetApps]; //function that retrieves data from Website and puts into the array - XMLdataArray.
}
XMLViewController *controller = [[XMLViewController alloc] initWithNibName:#"MedGearsApps" bundle:nil];
[self.navigationController pushViewController:controller animated:YES];
[controller release];
}
It works fine, but how can I make the view buttons functional with getting stuck. In other words, I just want the UIButton and other UIButtons to be functional whiles the thing works in the background.
I heard about performSelectorInMainThread but I can't put it to practice correctly.
You don’t understand the threading model much and you’re probably going to shoot yourself in the foot if you start adding asynchronous code without really understanding what’s going on.
The code you wrote runs in the main application thread. But when you think about it, you don’t have to write no main function — you just implement the application delegate and the event callbacks (such as touch handlers) and somehow they run automatically when the time comes. This is not a magic, this is simply a Cocoa object called a Run Loop.
Run Loop is an object that receives all events, processes timers (as in NSTimer) and runs your code. Which means that when you, for example, do something when the user taps a button, the call tree looks a bit like this:
main thread running
main run loop
// fire timers
// receive events — aha, here we have an event, let’s call the handler
view::touchesBegan…
// use tapped some button, let’s fire the callback
someButton::touchUpInside
yourCode
Now yourCode does what you want to do and the Run Loop continues running. But when your code takes too long to finish, such as in your case, the Run Loop has to wait and therefore the events will not get processed until your code finishes. This is what you see in your application.
To solve the situation you have to run the long operation in another thread. This is not very hard, but you’ll have to think of a few potential problems nevertheless. Running in another thread can be as easy as calling performSelectorInBackground:
[appDelegate performSelectorInBackground:#selector(GetApps) withObject:nil];
And now you have to think of a way to tell the application the data has been loaded, such as using a notification or calling a selector on the main thread. By the way: storing the data in the application delegate (or even using the application delegate for loading the data) is not very elegant solution, but that’s another story.
If you do choose the performSelectorInBackground solution, take a look at a related question about memory management in secondary threads. You’ll need your own autorelease pool so that you won’t leak autoreleased objects.
Updating the answer after some time – nowadays it’s usually best to run the code in background using Grand Central Dispatch:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// No explicit autorelease pool needed here.
// The code runs in background, not strangling
// the main run loop.
[self doSomeLongOperation];
dispatch_sync(dispatch_get_main_queue(), ^{
// This will be called on the main thread, so that
// you can update the UI, for example.
[self longOperationDone];
});
});
Use NSURLConnection's connectionWithRequest:delegate: method. This will cause the specified request to be sent asynchronously. The delegate should respond to connection:didReceiveResponse: and will be sent that message once the response is completely received.
You can make use of a background operation that gets pushed into the operation queue:
BGOperation *op = [[BGOperation alloc] init];
[[self operationQueue] addOperation:op];
[op release];
I've created specific "commands" that get executed in the background:
#implementation BGOperation
# pragma mark Memory Management
- (BGOperation *)init
{
if ((self = [super init]) != nil)
/* nothing */;
return self;
}
- (void)dealloc
{
self.jobId = nil;
[super dealloc];
}
# pragma mark -
# pragma mark Background Operation
- (void)main
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[appDelegate GetApps];
[pool release];
return;
}
#end
After completion it might be a good idea to send a notification to the main thread because the internal database has been changed.
It looks as if you might be using NSURLConnection inside your getApps method. If so, you should convert it to an asynchronous call.