Dispatch Queue and NSOperation queue - ios

I am creating a serial queue in which i add two task as shown below
dispatch_queue_t serial = dispatch_queue_create("com.apple.serial", DISPATCH_QUEUE_SERIAL);
**//Task 1**
dispatch_async(serial, ^{
[NMUserAPIManager getUserProfileData:^(NMUser *objUser) {
NSLog(#"Get User Profile .....");
_objUser = objUser;
}];
});
**//Task 2**
dispatch_async(serial, ^{
[NMUserAPIManager getUserRecentTransactionData:^(NSDictionary *responseDictionary) {
_accountTableView.hidden = NO;
[self recentTransactionSetup:responseDictionary];
NSLog(#"Get User Recent transaction");
dispatch_async(dispatch_get_main_queue(), ^{
[self reloadTableData];
});
}];
});
Inside that two task i am calling web service with NSURLSession. Problem is that before my Task 1 completion handle Task2 completion handle get called. According to theory by using serial queue each task waits for the previous task to finish before being executed. It my understanding is correct.

NSURLSession's already run on a background thread, so the issue you are seeing here is that as far as your serial queue is concerned once you call 'getUserProfileData:' technically the work for that block in your queue is finished because the NSURLSession is running on a different thread. If your main goal here is to simply call your second task after your first one completes I don't think you need your own queue you would probably be better off simply doing something like:
[NMUserAPIManager getUserProfileData:^(NMUser *objUser) {
NSLog(#"Get User Profile .....");
_objUser = objUser;
[self getUserTransactions];
}];
-(void)getUserTransactions
{
[NMUserAPIManager getUserRecentTransactionData:^(NSDictionary *responseDictionary) {
_accountTableView.hidden = NO;
[self recentTransactionSetup:responseDictionary];
NSLog(#"Get User Recent transaction");
dispatch_async(dispatch_get_main_queue(), ^{
[self reloadTableData];
});
}];
}
EDIT:
If you are looking for something a little more robust I would check out this post for how you can subclass NSOperation to make your own Asynchronous Operation which you can then use with an NSOperationQueue.

Related

In unit test, execute the block passed in queue with dispatch_asyc

If I dispatch_async a block on main queue like this:
-(void) myTask {
dispatch_async(dispatch_get_main_queue(), ^{
[self.service fetchData];
});
}
In unit test, I can execute the block passed in main queue by manually run the main loop like this:
-(void)testMyTask{
// call function under test
[myObj myTask];
// run the main loop manually!
[[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01]];
// now I can verify the function 'fetchData' in block is called
...
}
Now, I have another similar function which dispatch block to an sequential queue other than main queue:
-(void) myTask2 {
dispatch_async(dispatch_queue_create("my.sequential.queue", NULL), ^{
[self.service fetchData];
});
}
In unit test, how can I execute the block manually now?
-(void)testMyTask2{
// call function under test
[myObj myTask2];
// How to manually execute the block now?
}
=== Clarify ===
The reason why I want to manually execute is because I don't like any Wait-For-Timeout way doing the test. Because waiting time is depending on CPU speed, on different machines could be different. I'd like to manually execute the block passed to queue (the same way as how I did for the main queue test case) and then verify the result.
You could create the queue in your test function.
-(void) myTask2:(dispatch_queue_t*)queue {
dispatch_async(*queue, ^{
[self.service fetchData];
});
}
-(void)testMyTask2{
dispatch_queue_t queue = dispatch_queue_create("my.sequential.queue", NULL);
[myObj myTask2:&queue];
dispatch_sync(queue, ^{
});
}
(Just realised currentRunLoop is not needed)
For execute test in async block use XCTestExpectation class
-(void) myTask2 {
XCTestExpectation *expectation = [self expectationWithDescription:#"catch is called"];
dispatch_async(dispatch_queue_create("my.sequetial.queue", NULL), ^{
[self.serviceClient fetchDataForUserId:self.userId];
[expectation fulfill];
});
[self waitForExpectationsWithTimeout:Timeout handler:^(NSError *error) {
//check that your NSError nil or not
}];
}
Hope this help

How to make api calls synchronously in background?

I have four api calls to make. They should be in following order:
apiSyncDataToCloud;
apiSyncImagesToServer;
apiDeleteDataFromCloud;
apiSyncDataFromCloudInBackground;
Each one of them is to be called irrespective of the fact that previous one finishes successfully or fails.
Also, each one of them have success and failure completion blocks.
In success completion block database is updated.
All this process has to be performed in background and has to be done a no of times.
Api calls are of course performed in background but once a call completes database update is performed on main thread thereby freezing the app.
So, I went with several solutions:
Tried following code:
NSOperationQueue *queue = [NSOperationQueue new];
queue.maxConcurrentOperationCount = 1;
[queue addOperationWithBlock:^{
[self apiSyncDataToCloud];
}];
[queue addOperationWithBlock:^{
[self apiSyncImages];
}];
[queue addOperationWithBlock:^{
[self apiDeleteDataFromCloud];
}];
[queue addOperationWithBlock:^{
[self apiSyncDataFromCloudInBackground];
}];
But this only guarantees that api method calls will be performed in order. But their result follows no specific order. That is, method calls will be in the order specified but success block of apiSyncImagesToServer may be called before success block of apiSyncDataToCloud.
Then I went with following solution:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self apiSyncDataToCloud];
});
and in the success and failure blocks of apiSyncDataToCloud I have called apiSyncImagesToServer. This too did'nt work.
Now I am simply going with my last solution. I am just calling apiSyncDataToCloud.
In success completion block this method first updates the database and then calls other api.
In failure completion block this method simply makes the api call without updating the database.
For example-
structure of apiSyncDataToCloud is as follows:
-(void)apiSyncDataToCloud{
NSLog(#"method 1");
NSMutableDictionary *dicDataToBeSynced = [NSMutableDictionary dictionary];
dicDataToBeSynced = [self getDataToBeSynced];
if (dicDataToBeSynced.count!=0) {
if ([[StaticHelper sharedObject] isInternetConnected]) {
[[ApiHandler sharedObject] postRequestWithJsonString:API_SYNC_DATA_TO_CLOUD andHeader:[UserDefaults objectForKey:kAuthToken] forHeaderField:kAccessToken andParameters:dicDataToBeSynced WithSuccessBlock:^(NSURLResponse *response, id resultObject, NSError *error) {
NSLog(#"Data synced successfully to server");
[self updateColumnZSYNC_FLAGForAllTables];//updating db
[self apiSyncImagesToServer];//api call
} andFailureBlock:^(NSURLResponse *task, id resultObject, NSError *error) {
NSLog(#"Data syncing to cloud FAILED");
[self apiSyncImagesToServer];//simply make api call without updating db
}];
}
}else{
[self apiSyncImagesToServer];make api call even if no data to be synced found
}
}
Similary, inside apiSyncImagesToServer I am calling apiDeleteDataFromCloud.....
As a result my problem remained as it is. App freezes when it comes to success block updating db, downloading images...all operations being performed on main thread.
Plz let me know a cleaner and better solution.
You can create your own custom queue and call request one by one.
i.e.
dispatch_queue_t myQueue;//declare own queue
if (!myQueue) {//check if queue not exists
myQueue = dispatch_queue_create("com.queue1", NULL); //create queue
}
dispatch_async(myQueue, ^{[self YOUR_METHOD_NAME];});//call your method in queue block
If you want update some UI after receiving data then update UI on main Thread.
1) Better to use AFNetworking for this kind of situations. Because AFNetworking provides better way to handle Main & Background Threads.
AFNetworking supports success and failure blocks so you can do one by one WS Api calls from success and failure of previous WS Api call. So during this time period show progress HUD. Success of last API then update DB and hide progress HUD.
2) If you need to use NSOperationQueue and NSInvocationOperation
and follow this link. https://www.raywenderlich.com/76341/use-nsoperation-nsoperationqueue-swift
Api calls are of course performed in background but once a call
completes database update is performed on main thread thereby freezing
the app.
Then why not perform it in a separate queue?
Try using
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//your code
});
to perform time-consuming tasks and
dispatch_async(dispatch_get_main_queue(), ^{
//your code
});
to only update UI.

Wait for an async methods to finish in a for loop

I have a for loop containing three asynchronous methods, and I want to make some treatment after this 3 async methods are finished.
-(void)getAllUsersInformations{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for(User *user in users){
[self getUserInfo:user];
}
//Here, I want to reload the table view for example, after finishing the for loop (executing the whole three methods).
});
}
-(void)getUserInfo:(User*)user{
[self getInformations:user];
[self getExperiences:user];
[self getEducation:user];
}
Do you have any technic to have this result?
Thank you very much.
One GCD approach is to use dispatch_group. So, before you start an asynchronous task, call dispatch_group_enter, and then when the asynchronous task finishes, call dispatch_group_leave, and you can then create a dispatch_group_notify which will be called when the asynchronous tasks finish. You can marry this with a completion block pattern (which is a good idea for asynchronous methods, anyway):
If getInformations, getExperiences and getEducation are, themselves, all asynchronous methods, the first thing you need is some mechanism to know when they're done. A common solution is to implement a completion block pattern for each. For example:
// added completionHandler parameter which will be called when the retrieval
// of the "informations" is done.
- (void)getInformations:(User*)user completionHandler:(void (^)(void))completionHandler {
// do whatever you were before, but in the asynchronous task's completion block, call this
// completionHandler()
//
// for example
NSURLRequest *request;
[NSURLConnection sendAsynchronousRequest:request queue:nil completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
// handle the request here
// the important thing is that the completion handler should
// be called _inside_ the this block
if (completionHandler) {
completionHandler();
}
}];
}
Repeat this process for getExperiences and getEducation, too.
Then, you can use a dispatch group to notify you of when each of these three requests are done done, calling a completion block in getUserInfo when that takes place:
// added completion handler that will be called only when `getInformations`,
// `getExperiences` and `getEducation` are all done.
//
// this takes advantage of the completion block we added to those three
// methods above
- (void)getUserInfo:(User*)user completionHandler:(void (^)(void))completionHandler {
dispatch_group_t group = dispatch_group_create();
// start the three requests
dispatch_group_enter(group);
[self getInformations:user completionHandler:^{
dispatch_group_leave(group);
}];
dispatch_group_enter(group);
[self getExperiences:user completionHandler:^{
dispatch_group_leave(group);
}];
dispatch_group_enter(group);
[self getEducation:user completionHandler:^{
dispatch_group_leave(group);
}];
// this block will be called asynchronously only when the above three are done
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
if (completionHandler) {
completionHandler();
}
});
}
And you then repeat this process at the getAllUsersInformations:
// call new getUserInfo, using dispatch group to keep track of whether
// all the requests are done
-(void)getAllUsersInformations {
dispatch_group_t group = dispatch_group_create();
for(User *user in users){
dispatch_group_enter(group);
[self getUserInfo:user completionHandler:^{
dispatch_group_leave(group);
}];
}
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
}
Two final thoughts:
Having outlined all of that, I must confess that I would probably wrap these requests in concurrent/asynchronous custom NSOperation subclasses instead of using dispatch groups. See the "Configuring Operations for Concurrent Execution" section of the Concurrency Programming Guide. This is a more radical refactoring of the code, so I won't tackle that here, but it lets you constrain the number of these requests that will run concurrently, mitigating potential timeout issues.
I don't know how many of these user requests are going on, but you might want to consider updating the UI as user information comes in, rather than waiting for everything to finish. This is, again, a more radical refactoring of the code, but might lead to something that feels more responsive.
Try to do a block with completion, you can't do this with a for loop if the methods are async. you have to call getUserInfo one by one after the completion of the previous. I think this gonna be solved your problem.
-(void)getAllUsersInformations{
[self registerUserAtIndex:0];
}
- (void) registerUserAtIndex: (NSInteger ) userIndex
{
RegisterOperation *op = [[RegisterOperation alloc] initWithUser:[users objectAtIndex:userIndex]];
[RegisterOperation setResultCompletionBlock:^(BOOL *finished, NSInteger userIndex) {
dispatch_async(dispatch_get_main_queue(), ^{
if (userIndex++ < [users count] {
[self registerUserAtIndex:userIndex++];
} else {
[myTableView reloadData];
}
}];
[[NSOperationQueue mainQueue] addOperation:op];
}
Hope this will help you.
Rop Answer with swift:
func processData()
{
let group: dispatch_group_t = dispatch_group_create()
for item in data as! Object {
dispatch_group_enter(group)
item.process(completion: {() -> (Void) in
dispatch_group_leave(group)
})
}
dispatch_group_notify(group, dispatch_get_main_queue(), {
//Do whatever you want
})
}
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Background work
for(User *user in users){
[self getUserInfo:user];
}
dispatch_async(dispatch_get_main_queue(), ^{
//reload tableview , this is on main thread.
});
});

Waiting for nested AFNetworking calls and for loops to finish

So, I've been searching all over but didn't find a solution, or at least I couldn't apply it.
I've found this thread here on stackoverflow, but didn't succeed in implementing it in my code.
My issue is, that I need to know when nested AFNetworking calls and For loops are done. I've tried it with GCD groups, but with no luck.
The code looks like this:
{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_async(queue, ^{
[[JSON GET method using AFNetworking 2.0] success:^(NSArray *result) {
dispatch_group_async(group, queue, ^{
//do some work with the result
for (NSDictionary *resultPartDictionary in result) {
dispatch_group_async(group, queue, ^{
//do some more work with parts of the result
[[JSON GET method based on result] success:^(NSArray *result) {
dispatch_group_async(group, queue, ^{
//do some work
for (NSDictionary *resultPartDictionary in result) {
dispatch_group_async(group, queue, ^{
//do some work
[[JSON GET method based on result] success:^(NSArray *result) {
dispatch_group_async(group, queue, ^{
//do some work
for (NSDictionary *resultPartDictionary in result) {
dispatch_group_async(group, queue, ^{
//do some work
});
}
});
}];
});
}
});
}];
});
}
});
}
});
}
Right now, everything works. I'm handling Core Data inside the blocks, so I needed MOCs for every thread, which works as well.
The only thing I'd like to know is how to know when all these blocks finish.
Thank you!
EDIT
So, I've tried using dispatch_group_enter(group) and dispatch_group_leave(group), but it seems to me, that it's just not possible with this embedded architecture. Because of the For loops, the "leave" notifications are either too many, which causes an exception or not enough and the dispatch_group_notify returns too early.
Any ideas on this?
You are looking for dispatch_group_notify and dispatch_group_enter/dispatch_group_leave.
dispatch_group_notify executes the given block in the given queue, when every block in the group is finished.
dispatch_group_enter increases the current count of executing tasks in the group. Every dispatch_group_enter must be balanced with a call to dispatch_group_leave.
dispatch_group_leave decreases the current count of executing tasks in the group.
So, you should trick dispatch_group_notify with increase the number of the tasks in the group before your network calls start and decrease it when everything finished. To achieve this, call dispatch_group_enter before dispatch_async and call dispatch_group_leave in the last thread. Since you know the element count of the every result array, you can check if the current thread is the last one.
dispatch_group_enter(group); // Increases the number of blocks in the group.
dispatch_async(queue, ^{
// Make your AFNetworking calls.
dispatch_group_async(group, queue, ^{
//do some work.
if (isLastThread)
dispatch_group_leave(group); // Decreases the number of blocks in the group.
});
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{ // Calls the given block when all blocks are finished in the group.
// All blocks finished, do whatever you like.
});

How to timeout an asynchronous method when ran synchronously

This is essentially what I'm doing to run an asynchronous method synchronously:
This essentially works when called once, but when called multiple times, it will eventually stay inside the while loop and never get signaled. Any ideas on how to set a timer to eventually time out after sometime?
__block SomeClass *result = nil;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0UL);
dispatch_async(queue, ^{
[[SomeManager sharedInstance] someMethodWithCallback:^(id responseObject, NSError *error) {
if (!error) {
result = (SomeClass *)ResponseObject;
}
dispatch_semaphore_signal(semaphore);
}];
});
// wait with a time limit
while (dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW)) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0]];
}
dispatch_release(semaphore);
Thanks
That looks kind of like GCD abuse to me. ;) Are you running the run loop because this is executing on the main thread? Why not just use a dispatch_async() from your completion handler to invoke a handler on the main thread? eg:
- (void)handleDataReady: (id) results error: (NSError *) error {
// update your app
}
- (void)performAsyncUpdate {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0UL);
dispatch_async(queue, ^{
[[SomeManager sharedInstance] someMethodWithCallback:^(id responseObject, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
[self handleDataReady:responseObject error:error];
}];
});
}
If you really want to make it synchronous, i.e. blocking the calling thread until the operation completes then use the following pattern (of course you want to avoid blocking threads if possible)
NSCondition *waitCondtion = [NSCondition new];
__block BOOL completed = NO;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0UL);
dispatch_async(queue, ^{
[[SomeManager sharedInstance] someMethodWithCallback:^(id responseObject, NSError *error) {
if (!error) {
result = (SomeClass *)ResponseObject;
}
[waitCondtion lock];
completed = YES;
[waitCondition signal];
[waitCondition unlock];
}];
});
[waitCondtion lock];
if (!completed)
[waitCondtion wait];
[waitCondition unlock];
You can also use "waitUntilDate:" to timeout the wait after a period.
However, this pattern only works as long as the "someMethodWithCallback does not call its callback block on the same thread that is being blocked. I have copied your code because it is not obvious how "someMethodWithCallback" is implemented. Since this method is using an asynchronous pattern, then it must be doing something asynchronously therefore why are you calling it inside a dispatch_async? What thread will it call its callback block on?
You should "fill" the completion handler with whatever code you require to process the result when the completion handler finished (and also completely removing that run loop).
In order to "abort" an asynchronous operation, you should provide a cancel message which you send the asynchronous result provider.
In your case, since you have a singleton, the cancel message would have to be send like this:
[[SomeManager sharedInstance] cancel];
When the operation receives the cancel message, it should as soon as possible abort its task and call the completion handler with an appropriate NSError object indicating that it has been cancelled.
Note, that cancel messages may be asynchronous - that means, when it returns, the receiver may still execute the task.
You may achieve a "timeout" with setting up a timer, which sends the cancel message the operation, unless it has been invalidated when the operation finished.

Resources