Creating block queues with completion - ios

I want to create 2 async queues with completion blocks and after finished this blocks I want to run some action. I can not achieve it with this code. Where my bad?
dispatch_queue_t queue = dispatch_queue_create("com.company.queue", 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
// block 1
dispatch_group_async(group, queue, ^{
[[WebRequests sharedInstance] request:#{#"type" : [NSNumber numberWithInt:request_uploadAdv], #"adv" : adv} withCompletion:^(id response) {
BOOL success = [response boolValue];
NSLog(#"done1 text");
// block 1 Done
}];
});
// block 2 //картинки
dispatch_group_async(group, queue, ^{
[self getImagesForAdv:adv completion:^(NSArray *images) {
[[WebRequests sharedInstance] uploadPhotos:images completion:^(BOOL success) {
uploadImagesSuccess = YES;
NSLog(#"done1 2\n");
// block 2 Done
}];
}];
});
dispatch_group_notify(group, queue, ^{
printf("all tasks are finished!\n");
});

First, you're missing a }); in there somewhere. Second, there's no need for the outer dispatch_group_async call anyway. Assuming it's there because you want these things to execute with background priority, you can do this instead:
dispatch_queue_t queue = dispatch_queue_create("com.company.queue", 0);
dispatch_set_target_queue(queue, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0));
dispatch_group_t group = dispatch_group_create();
// block 1
dispatch_group_async(group, queue, ^{
[[WebRequests sharedInstance] request:#{#"type" : [NSNumber numberWithInt:request_uploadAdv], #"adv" : adv} withCompletion:^(id response) {
BOOL success = [response boolValue];
NSLog(#"done1 text");
// block 1 Done
}];
});
// block 2 //картинки
dispatch_group_async(group, queue, ^{
[self getImagesForAdv:adv completion:^(NSArray *images) {
[[WebRequests sharedInstance] uploadPhotos:images completion:^(BOOL success) {
uploadImagesSuccess = YES;
NSLog(#"done1 2\n");
// block 2 Done
}];
}];
});
dispatch_group_notify(group, queue, ^{
printf("all tasks are finished!\n");
});

Related

dispatch_semaphore_wait blocked my network request callback

-(void)init{
self.sema = dispatch_semaphore_create(1)
}
-(void)main{
//sending one message is fine
[self preSendMessage:#"hi"];
//deadlock happen when sending multiple msg in a short time.
for(int i = 0; i < 10; i++){
[self preSendMessage:#"yo"];
}
}
- (void)preSendMessage:(NSString*)msg
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_semaphore_wait(self.sema, DISPATCH_TIME_FOREVER);
RACSignal* signal = [self sendMessage:msg];
#weakify(self);
[signal subscribeNext:^(id x) {
} error:^(NSError *error) {
#strongify(self);
dispatch_semaphore_signal(self.sema);
} completed:^{
#strongify(self);
dispatch_semaphore_signal(self.sema);
}];
});
}
Some informations:
In sendMessage function, I am using AFNetworking.
When I remove the semaphore logic, it works fine, I need this because need to control the sequence of sending msgs.
[Update] my semaphore is init with value 1, so It would run at the first time
My Target is to ensure the code inside preSendMessage execute when the previous one is completed/error
Edit: you should use dispatch_semaphore_create(0)
You should put dispatch_semaphore_wait(self.sema, DISPATCH_TIME_FOREVER); after dispatch_async(....)
Something like this:
- (void)preSendMessage:(NSString*)msg{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
RACSignal* signal = [self sendMessage:msg];
#weakify(self);
[signal subscribeNext:^(id x) {
} error:^(NSError *error) {
#strongify(self);
dispatch_semaphore_signal(self.sema);
} completed:^{
#strongify(self);
dispatch_semaphore_signal(self.sema);
}];
});
dispatch_semaphore_wait(self.sema, DISPATCH_TIME_FOREVER);
}
You are calling dispatch_semaphore_wait in the wrong order. Your code subscription does not get executed, because your semaphore is in a wait state. You should call your dispatch_semaphore_wait right after dispatching to background thread.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
RACSignal* signal = [self sendMessage:msg];
#weakify(self);
[signal subscribeNext:^(id x) {
} error:^(NSError *error) {
#strongify(self);
dispatch_semaphore_signal(self.sema);
} completed:^{
#strongify(self);
dispatch_semaphore_signal(self.sema);
}];
});
dispatch_semaphore_wait(self.sema, DISPATCH_TIME_FOREVER);
You tell your app to wait for the semaphore, so your app waits for the semaphore. It doesn't execute any code after the wait call.
You need to set up everything, including the callbacks that will signal, and then you wait for the signal.

How to Custom group notify for blocks of functions

Asynchronous block be called in function, add these multiple functions into a group,process all results of blocks when all block execute completely.Is there a common and smart method to implement?
like dispatch_group_notify ,completionBlock
dispatch_group_t _blockTaskGroup;
.....
if (!_blockTaskGroup) {
_blockTaskGroup = dispatch_group_create();
}
dispatch_group_notify(_blockTaskGroup, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
NSLog(#"finally!");
});
....
dispatch_group_enter(_blockTaskGroup);
[XXXXX computeInBackground:xx completion:^{
NSLog(#"1 done");
dispatch_group_leave(group);
}];
//
dispatch_group_enter(_blockTaskGroup);
[XXXXX computeInBackground:xx completion:^{
NSLog(#"2 done");
dispatch_group_leave(_blockTaskGroup);
}];
//
dispatch_group_enter(_blockTaskGroup);
[XXXXX computeInBackground:xx completion:^{
NSLog(#"3 done");
dispatch_group_leave(_blockTaskGroup);
}];

Is it safe to call XCTestExpectation fulfill method on background thread?

I am using XCTestExpectation in a lot of tests and sometimes (very randomly) some expectations are not fulfilled (although I am sure they should be).
While investigating this problem I have noticed that some expectations are fulfilled in a main thread and some are fulfilled in a background thread. And so far these problems are with the ones fulfilled in a background thread.
Is it safe to fulfill expectations from a background thread? I could not find any explicit information about that.
Below is an example of how I use XCTestExpectation:
__block XCTestExpectation *expectation = [self expectationWithDescription:#"test"];
[self doSomethingAsyncInBackgroundWithSuccess:^{
[expectation fullfill];
}];
[self waitForExpectationsWithTimeout:10.0 handler:^(NSError *error) {
expectation = nil;
if (error) {
NSLog(#"Timeout Error: %#", error);
}
}];
It's not documented anywhere that XCTestExpectation is thread-safe. due to there being no official documentation on the matter you can only guess by creating test examples:
- (void)testExpectationMainThread;
{
__block XCTestExpectation *expectation = [self expectationWithDescription:#"test"];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[expectation fulfill];
});
[self waitForExpectationsWithTimeout:2 handler:^(NSError * _Nullable error) {
NSLog(#"%#", error);
}];
}
- (void)testExpectationStartMainThreadFulfilBackgroundThread;
{
__block XCTestExpectation *expectation = [self expectationWithDescription:#"test"];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, kNilOptions), ^{
[expectation fulfill];
});
[self waitForExpectationsWithTimeout:2 handler:^(NSError * _Nullable error) {
NSLog(#"%#", error);
}];
}
- (void)testExpectationBackgroundThread;
{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, kNilOptions);
__block XCTestExpectation *expectation;
dispatch_sync(queue, ^{
expectation = [self expectationWithDescription:#"test"];
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), queue, ^{
[expectation fulfill];
});
[self waitForExpectationsWithTimeout:2 handler:^(NSError * _Nullable error) {
NSLog(#"%#", error);
}];
}
Here it does not crash or cause a problem however due to the lack of official documentation it is probably safer to stick to the same queue to fulfil.
you should really be stubbing the method doSomethingAsyncInBackgroundWithSuccess and provide the app with local "dummy" data.
Your unit tests should not rely on network as it is something which is variable.
You should be executing the completion block of doSomethingAsyncInBackgroundWithSuccess on the main thread (or at least provide a way to call back consistently on the same thread), you can easily do this with GCD.
- (void)doSomethingAsyncInBackgroundWithSuccess:(void (^)(void))completion;
{
dispatch_async(dispatch_get_main_queue(), ^{
completion();
});
}
or use NSOperationQueue mainQueue
- (void)doSomethingAsyncInBackgroundWithSuccess:(void (^)(void))completion;
{
[NSOperationQueue.mainQueue addOperationWithBlock:^{
completion();
}];
}

Would I use dispatch in block for MBProgressHud?

In the MBProgressHud documentation it states to use this inside of a dispatch like so:
[MBProgressHUD showHUDAddedTo:self.view animated:YES];
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
// Do something...
dispatch_async(dispatch_get_main_queue(), ^{
[MBProgressHUD hideHUDForView:self.view animated:YES];
});
});
Which is completely understandable considering you don't want it to boggle up the main thread. But could I just do this instead when using a block:
[MBProgressHUD showHUDAddedTo:self.view animated:YES];
[object deleteInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (error)
{
}
else
{
[MBProgressHUD hideHUDForView:self.view animated:YES];
}
}];
Or would I still have to use dispatch?
Change your code to be like this:
[MBProgressHUD showHUDAddedTo:self.view animated:YES];
[object deleteInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
NSLog(#"Am I running on the main thread: %#", [NSThread isMainThread] ? #"YES": #"NO");
if (error)
{
}
else
{
}
}];
if it logs "YES" then you don't need to run [MBProgressHUD hideHUDForView:self.view animated:YES]; on the main thread, otherwise you need to use
dispatch_async(dispatch_get_main_queue(), ^{
[MBProgressHUD hideHUDForView:self.view animated:YES];
});
Update:
blocks are run on whatever thread they've been called from, notice following example:
- (void)viewDidLoad {
[super viewDidLoad];
[self longRunningProcessWithCompletionBlock:^{
NSLog(#"Is running on the main thread? %#", [NSThread isMainThread] ? #"YES" : #"NO");
}];
}
- (void)longRunningProcessWithCompletionBlock:(void (^)(void))completionBlock {
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//this is running on the concurrent thread, so does completionBlock() as it has been called on a concurrent thread.
dispatch_async(concurrentQueue, ^{
[NSThread sleepForTimeInterval:3];
completionBlock();
});
}
So Basically the result of above will be "Is running on the main thread? NO"
Again I have exact same call on viewDidLoad:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self longRunningProcessWithCompletionBlock:^{
NSLog(#"Is running on the main thread? %#", [NSThread isMainThread] ? #"YES" : #"NO");
}];
}
But this time, I'm calling completionBlock of longRunningProcessWithCompletionBlock on the main thread as follow:
- (void)longRunningProcessWithCompletionBlock:(void (^)(void))completionBlock {
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(concurrentQueue, ^{
[NSThread sleepForTimeInterval:3];
//notice the difference, this time we are calling the completionBlock on the main thread, so that block will be running on the main thread
dispatch_async(dispatch_get_main_queue(), ^{
completionBlock();
});
});
}
This time because we have called the completion block on the main thread, the result will be Is running on the main thread? YES
So in a nutshell, block does not guarantee that they are getting executed on the main thread! but they can guarantee that they will be executed on whatever thread they've been called from.
In your case Parse.com developers are calling the completion handler block of deleteInBackgroundWithBlock on the main thread and that's why you saw that log was "yes".So you just need to call [MBProgressHUD hideHUDForView:self.view animated:YES]; without dispatch_async(dispatch_get_main_queue(), ^{ }); (as it is already on the main thread and this is an extra unnecessary step)

Activity indicator not animating when creating a dispatch group in iOS

I want to display an activity indicator while performing some network calls in a dispatch_group_asyc block. But activity indicator only shows when the block finishes. I'm creating a dispatch_group_t because I need to get the result of the network calls before performing some other tasks. This is a simplified version of my code:
- (BOOL)doNetCall
{
[activityIndicator startAnimating];
__block BOOL netResult = NO;
dispatch_queue_t queue = dispatch_queue_create(netQueue, NULL);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group,queue,^{
netResult = [service queryService];
});
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
dispatch_release(group);
dispatch_release(queue);
[activityIndicator stopAnimating];
if (netResult) {
// Perform some tasks
}
else {
[self showAlertView];
}
return netResult;
}
What am I doing wrong? Thanks!
EDIT: I need the method to wait until the block finishes in order to return the result I get
You should use activity indicator in this way:
[activityIndicator startAnimating];
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
// Do something...
dispatch_async(dispatch_get_main_queue(), ^{
[activityIndicator stopAnimating];
});
});
If you're using group, just rewrite your code a little bit.
There doesn't appear to be any need for a dispatch group with what you are doing. Try this:
- (void)buttonClicked {
[activityIndicator startAnimating];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
BOOL netResult = [service queryService];
dispatch_async(dispatch_get_main_queue(), ^{
[activityIndicator stopAnimating];
if (netResult) {
// perform some tasks
} else {
// show alert
}
});
});
}
If you have your queue, replace the call to dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) with your own queue.

Resources