Adding a "Final" NSOperation to a Queue with Undetermined Number of Operations - ios

I'm using AFNetworking as my network stack to communicate with a web service and populate a local data store. During synchronization runs, I have an array of API endpoints to run through, and when that run is complete, I add a final operation, which takes the resulting JSON to fill up the database.
The problem I'm having is that the result of some of those JSON-fetching operations requires me to call other endpoints, and now I don't know when I should add that "Final" operation.
The way I have things working now, I have a series of primary operations, and then add the "final" operation. During that time, the primaries have returned and caused me to create secondary operations, like so:
* Primary Fetch Operation A
* Primary Fetch Operation B
* Final Operation
* Secondary Fetch Operation B1
I need to figure out how to ensure that "Final Operation" is always going to run last.
One thing I've tried is adding an observer to the operation queue's operationCount property, but it seems that it can run down to 0 before a secondary operation is added.

Unfortunately I believe the observer won't work, AFNetworking invokes callbacks from the completionBlock of NSOperation and this means that the operation is already finished and removed from the queue, which explains why you have reached a operationCount of 0 before submit the secondary operation.
You could use a dispatch_group_t to accomplish this, before scheduling an operation (primary or secondary) you enter the group (dispatch_group_enter) and then when the operation completes you leave the group (dispatch_group_leave). If the finished operation requires a secondary operation before leaving the group follow the same pattern, enter the group again and schedule the secondary operation. Finally you will be notified (dispatch_group_notify) when all the operations have completed making the perfect time to schedule the final operation.

Related

how to determine whether a queue is empty in Simevents and do some actions based on this observation?

I want to model a queue with vacations. When the queue is empty, the server will have a period of vacation with certain distribution.(I can use use gate to block the server ) So I need to get the data of the number of entity in queue block. Could you please tell me how to do that?
Many thanks.
The "number of entities in the Queue" can be found in the 'Statistics' tab of the Queue's properties.
Enabling it (clicking it's checkbox) will enable the signal of interest on the block (#n) that can be connected to other Simulink blocks.
Connect the #n signal to "compare to constant" block to create a boolean signal that indicates if the queue is / is not empty.

How can query in FMDB while it's in an huge updating task

Please see my code below.
In our requirement, we have a lot of db updates at the beginning,
As I understand,
I doubt if the task A will let B wait until A is finished. if it works as I am saying, then I hope the Task A can be paused and I do Task B, that will be perfect
DO I understand correctly?
It will be great if I can query as B whenever I want and don't wait A finished. (the old DB is not empty, even not updated, it can be in use)
Thanks!
FMDatabaseQueue *queue = [[FMDatabaseQueue alloc]init];
// A
[queue inDatabase:^(FMDatabase *db, BOOL *rollback) {
//a huge amount of inserts and updates
}];
// B
[queue inDatabase:^(FMDatabase *db, BOOL *rollback) {
// some easy querying task
}];
If you initiate two Queues at the same time and If it access same table then there may be chance of Deadlock occurrence.
FMDatabaseQueue will run the blocks on a serialized queue. So if you call FMDatabaseQueue's methods from multiple threads at the same time, they will be executed in the order they are received.
Note: The calls to FMDatabaseQueue's methods are blocking. So even though you are passing along blocks, they will not be run on another thread.
I suggest working on a clone version of your database. If update task -> clone database by copy file, point reading path to clone version and points reading path to the original DB when finished update. But you should handle another thing like merge data when you make a change on working copy

Multiple objects waiting for the same API response

I have an API code, which loads a data necessary for my application.
It's as simple as:
- (void) getDataForKey:(NSString*) key onSuccess:(id (^)())completionBlock
I cache data returned from server, so next calls of that functions should not do network request, until there is some data missing for given key, then I need to load it again from server side.
Everything was okey as long as I had one request per screen, but right now I have a case where I need to do that for every cell on one screen.
Problem is my caching doesn't work because before the response comes in from the first one, 5-6 more are created at the same time.
What could be a solution here to not create multiple network request and make other calls waiting for the first one ?
You can try to make a RequestManager class. Use dictionary to cache the requesting request.
If the next request is the same type as first one, don't make a new request but return the first one. If you choose this solution, you need to manager a completionBlock list then you will be able to send result to all requesters.
If the next request is the same type as first one, waiting in another thread until the first one done. Then make a new request, you API will read cache automatically. Your must make sure your codes are thread-safe.
Or you can use operation queues to do this. Some documents:
Apple: Operation Queues
Soheil Azarpour: How To Use NSOperations and NSOperationQueues
May be there will be so many time consuming solutions for this. I have a trick. Create a BOOL in AppDelegate, its default is FALSE. When you receive first response, then set it TRUE. So when you go to other screen and before making request just check value of your BOOL variable in if condition. If its TRUE means response received so go for it otherwise in else don't do anything.

Concurrent task in AFNetworking

Currently I am running 3 task concurrently using AFNetworking.But my problem is that I need to refresh tableview once all the above three task complete.But as in AFNetworking all the operation are async. So any operation can finish first. I am not getting a point where I need to refresh my tableview. I am planning to do some critical section type implementation.Is there can other way to accomplish the above.
Use the built-in batched request operation feature of AFNetworking. The completion handler for the batch would include the logic to refresh your data source once all of the operations have finished.
Maybe having some sort of 'active requests array' is a solution for you.
Every time you make a request, add it to the (mutable) array.
When the request finishes, remove it from the array.
Every time a request finishes, check the array length (count).
If your array has become empty again, all request have been finished and you can reload your tableview.

a bunch of requests with gcd

So the task is the following:
1)I have a track ID, I need to ask the server for all the track data
2)parse response (here I also have an album ID)
3)now I have an album ID, I need to ask the server for all the album data
4)parse response (here I also have an artist ID)
5)now I have an artist ID, I need to ask the server for all the artist data
I wonder what is the right way to do this with gcd. 3 dispatch_sync-s inside dispatch_async?
I want all this to be one operation, run in the background, so at first I thought about NSOperation, but all callbacks, parsing, saving to core data need to happen on background thread, so I'd have to create a separate run loop for callbacks to make sure it will not be killed before I get a response and will not block ui.
so the question is how should I use gcd here, or is it better to go with nsoperation and a runloop thread for callbacks? thanks
I would suggest using NSOperation and callbacks executed on the main thread.
If you think about it, your workflow is pretty sequential: 1 -> 3 -> 5; the parsing steps (2 and 4) are not presumably that expensive so that you want to execute them on a separate thread (I guess they are not expensive at all and you can disregard parsing time compared to waiting time for network communication).
Furthermore, if you use a communication framework like AFNetworking (or even NSURLConnection + blocks) your workflow will be pretty easy to implement:
retrieve track data
in "retrieve track data" response handler, get album id, then send new request for "album data";
in "retrieve album data" response handler, get artist id, and so on...

Resources