Best practice for writing a resource from two different processes in objective-c

I have a common objective-c pattern / practice of a question regarding a problem that I am trying to solve with my application. I could not find a similar objective-c focused question / answer here, though.

My application contains a mutable array of objects, which I call Records. An application collects records and places them in this array in one of two ways:

  • It reads data from a SQLite database, available locally in the application window. Reading usually happens very quickly.
  • It requests data asynchronously from the web service, waits for completion, then analyzes the data. Reading can be quick, but often it is not.

Sometimes an application reads from a database (1) and requests data from a web service (2) almost at the same time. It often happens that (1) ends before (2) ends and adding entries to the mutable array does not lead to a conflict.

I'm worried that at some point, my SQLite reading process will take a little longer than expected, and it will try to add objects to the mutable array at the same time that the async query completes and does the same; or vice versa. These are edge cases that are difficult to verify, but this will most likely lead to a crash in my application or at least cause problems with my array of records.

I should also indicate that the entries should be concatenated into a mutable array. For example: if (1) starts first and returns 10 records, then soon after (2) finishes and returns 5 records, my modified array will contain all 15 records. I combine data, not rewrite it.

What I want to know:

  • Is it safe for me to add objects to the same instance of a mutable array when processes (1) or (2) end?
  • Is there a good template / practice for this kind of processing in objective-c?
  • Does this include blocking access to the mutable array, so when (1) adds objects to it (2), you cannot add any objects until (1) is done with it?

I appreciate any information you could share.

[EDIT # 1]

For posterity, I found this URL to help you understand how to use NSOperations and NSOperationQueue. It is a bit outdated, but it works nonetheless:

http://www.raywenderlich.com/19788/how-to-use-nsoperations-and-nsoperationqueues

In addition, he does not speak specifically about the problem that I am trying to solve, but the example that he uses is practical and understandable.

[EDIT # 2]

I decided to go with the approach suggested by danh, where I will read locally and hit my web service as needed after completing the local reading (which should be fast anyway). Taht said: I'm going to try to avoid synchronization problems at all. What for? Because Apple says so:

http://developer.apple.com/library/IOS/#documentation/Cocoa/Conceptual/Multithreading/ThreadSafety/ThreadSafety.html#//apple_ref/doc/uid/10000057i-CH8-SW8

Avoid syncing everything

For any new projects you are working on, and even for existing projects, creating your code and data structures to avoid the need for synchronization is the best solution. Although locks and other synchronization tools are useful, they affect the performance of any application. And if the overall design causes a high conflict between specific resources, your threads can wait even longer.

The best way to implement concurrency is to reduce the interaction and interdependencies between your parallel tasks. If each task works with its own personal data set, it does not need to protect this data with locks. Even in situations where two tasks share a common data set, you can look at ways to separate this set or provide each task with its own copy. Of course, copying datasets also has its costs, so before you make a decision, you will have to weigh these costs for synchronization costs.

+4
source share
4 answers

Is it safe for me to add objects to the same instance of a mutable array when processes (1) or (2) end?

Absolutely not. NSArray, along with the rest of the collection classes, is not synchronized . You can use them in combination with some kind of lock when adding and deleting objects, but this is definitely much slower than just creating two arrays (one for each operation) and combining them when both are completed.

Is there any good template / practice for implementing this kind of processing in objective-c?

Unfortunately not. The most you can think of is to turn off the Boolean language or increase the integer to a certain number in the general callback. To understand what I mean, here is a small pseudo-code:

- (void)someAsyncOpDidFinish:(NSSomeOperation*)op { finshedOperations++; if (finshedOperations == 2) { finshedOperations = 0; //Both are finished, process } } 

Does this include blocking access to the mutable array, so when (1) adds objects to it (2), is it impossible to add any objects until (1) is done with it?

Yes, see above.

+2
source

You must either block array changes or plan your changes in the main thread. SQL fetch probably works in the main thread, so in your remote fetch code you can do something like:

 dispatch_async(dispatch_get_main_queue(), ^{ [myArray addObject: newThing]; }]; 

If you add a bunch of objects, this will be slow, since it places a new task in the scheduler for each entry. You can bind entries in a separate array in the stream and add a temp array using addObjectsFromArray: if so.

+2
source

Personally, I would be inclined to have a parallel NSOperationQueue and add two lookup operations, one for the database operation, one for the network operation. Then I would have a dedicated sequential queue to add entries to the NSMutableArray , which each of the two parallel searches would use to add entries to the mutable array. Thus, you have one queue for adding records, but it is loaded from two search operations running on another parallel queue. If you need to know when two parallel search operations are performed, I would add the third operation to this parallel queue, set its dependencies as two search operations that automatically worked when two search operations were performed.

+1
source

In addition to the good suggestions above, consider running GET and sql at the same time.

 [self doTheLocalLookupThen:^{ // update the array and ui [self doTheServerGetThen:^{ // update the array and ui }]; }]; - (void)doTheLocalLookupThen:(void (^)(void))completion { if ([self skipTheLocalLookup]) return completion(); // do the local lookup, invoke completion } - (void)doTheServerGetThen:(void (^)(void))completion { if ([self skipTheServerGet]) return completion(); // do the server get, invoke completion } 
+1
source

All Articles