iOS stop global queue from running - ios

Code:
dispatch_async(
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
[ServerAPI API_GetChatList:self
withUserId:[self getUserIDFromUserDefaults]
withScheduleId:strGroupId
withType:#"group"];
dispatch_async(dispatch_get_main_queue(), ^{
[self performSelector:#selector(getChatList)
withObject:nil
afterDelay:10];
});
});
I used dispatch global queue to call a method for every 10 seconds.It is working fine.The problem is global queue is keep running in other controllers too.How do i stop this from running?any help will be appreciated.

You can keep a BOOL property,before every call, you check this property. When you want to stop, set it to YES
#property (atomic) BOOL stop;
Function
-(void)getChatList {
if (self.stop) {
return;
}
dispatch_async(
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
//Do something
dispatch_async(dispatch_get_main_queue(), ^{
if (!self.stop) {
[self performSelector:#selector(getChatList)
withObject:nil
afterDelay:10];
}
});
});
}

Related

How to execute second dispatch_async when previous first dispatch_async is finished?

I would like to add a dispatch_async sequentially but I would like them not to be launched randomly.
I would like for example :
dispatch_async 1 begins...
dispatch_async 1 ends.
dispatch_async 2 begins...
dispatch_async 2 ends.
dispatch_async 3 begins...
dispatch_async 3 ends.
I need to update a sqlite, and the informations in the first dispatch are necessary for the second dispatch...
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(#"%#",[connection currentRequest]);
NSLog(#"connectionDidFinishLoading");
NSError* error;
NSString *responseKey = [self getResponseKey:connection];
NSDictionary* response = [NSJSONSerialization JSONObjectWithData:[receivedData objectForKey:responseKey] options:kNilOptions error:&error];
//NSLog(#"%#", response);
if (error)
{
NSLog(#"Error: %#", error);
NSLog(#"Error: Response strange format, not an NSArray and not a NSString!\n%#", [[NSString alloc] initWithData:[receivedData objectForKey:responseKey] encoding:NSUTF8StringEncoding]);
}
NSLog(#"connection url : %#", connection.currentRequest.URL);
if ([[NSString stringWithFormat:#"%#", [connection currentRequest]] rangeOfString:#"getSynchroGuest?"].location != NSNotFound)
{
NSLog(#"response success");
if ([[response valueForKey:#"lignes"] isKindOfClass:[NSArray class]])
{
if ([[response valueForKey:#"lignes"] count] > 0)
{
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
//Background Thread
[self fillDataBaseWithDict:response];
nbTotal = nbTotal + PACKET_FOR_SYNC;
[self WebServiceSynchroGuest:self.activityIndicator withSynchroBtn:synchroBtn withNbTotal:nbTotal];
});
}
}
...
Thanks in advance.
SOLUTION:
dispatch_async(serialDispatchQueue, ^{
[self fillDataBaseWithDict:response];
nbTotal = nbTotal + PACKET_FOR_SYNC;
dispatch_async(dispatch_get_main_queue(), ^(void){
[self WebServiceSynchroGuest:self.activityIndicator withSynchroBtn:synchroBtn withNbTotal:nbTotal];
});
});
define your own serial queue
and add code like this
dispatch_queue_t yourSerialQueue = dispatch_queue_create("com.testcompany.testproduct.testserialqueue", DISPATCH_QUEUE_SERIAL);
dispatch_async(yourSerialQueue, ^{ /* first task */ });
dispatch_async(yourSerialQueue, ^{ /* second task to be executed after first task */ });
serial queue guarantees that the tasks will be exevuted in the order they are submitted and in a serial manner(one at a time)
To take your Q literally, you have to nest the calls:
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
^(void)
{
// do some work
…
// finished here
// The next async code
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
^(void)
{
// do some work
…
// finished here
// and so on
}
}
But you should really consider to use a custom serial queue or NSOperation et al.
With a serial Q:
dispatch_queue_t stepByStepQueue = dispatch_queue_create("com.you.taks", NULL);
dispatch_async(stepByStepQueue,
^(void)
{
// Step
});
dispatch_async(stepByStepQueue,
^(void)
{
// By
});
dispatch_async(stepByStepQueue,
^(void)
{
// Step
});
You can put all dispatch_async as a Serial Queue and execute one by one
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
// block1
NSLog(#"Block1");
[ NSThread sleepForTimeInterval:5.0];
NSLog(#"Block1 End");
});
dispatch_group_async(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
// block2
NSLog(#"block 2");
[ NSThread sleepForTimeInterval:10.0];
NSLog(#"Block2 End");
});
dispatch_group_async(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
// block3
NSLog(#"block 3");
[ NSThread sleepForTimeInterval:15.0];
NSLog(#"Block3 End");
});
dispatch_group_notify(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
// block4
NSLog(#"block 4");
[ NSThread sleepForTimeInterval:20.0];
NSLog(#"Block4 End");
});
Best approach would be to use NSOperations & add them in queue.so it would be called in sequence of completion
But if you want to do it in same way then define completion blocks & add your dispatch_async2 & dispatch_Async3 in completion blocks & call those completion blocks in end.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// do some long running processing here
// Check that there was not a nil handler passed.
if( completionHandler1 )
{
completionHandler1();
}
});
});
add code like this
You can set PRIORITY as well.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
});
});
What you want is easily achieved with Operation instead of GCD. Operation class has addDependency(_ op: Operation) function. Put your code in BlockOperation instances, add dependencies and run them in OperationQueue

How to call second function after first function excecution completed?

I have 3 function in my code.
The code i did for manage queue for function execution.
[self firstMethodWithOnComplete:^{
[self SecongMethodWithOnComplete:^{
dispatch_async(dispatch_get_main_queue(), ^{
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(CallThirdMethod) userInfo:nil repeats:NO];
});
}];
}];
First And Second Function
- (void)firstMethodWithOnComplete:(void (^)(void))onComplete {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
//processing here.....
[self CallFirstMethod];
onComplete();
});
}
- (void)SecongMethodWithOnComplete:(void (^)(void))onComplete {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
//processing here.....
[self CallSecondMethod];
onComplete();
});
}
The problem is i am unable to manage their execution. I want execution order such in a way that second function only execute if first is over and third execute if second execution over.
Please help me to solve this or give any appropriate suggestions.
You can use dispatch groups for this kind of requirement, Below i am posting example code which i have used
__block NSError *configError = nil;
__block NSError *preferenceError = nil;
// Create the dispatch group
dispatch_group_t serviceGroup = dispatch_group_create();
dispatch_group_enter(serviceGroup);
// Start the first async service
dispatch_group_leave(serviceGroup);
dispatch_group_enter(serviceGroup);
// Start the second async service
dispatch_group_leave(serviceGroup);
dispatch_group_notify(serviceGroup,dispatch_get_main_queue(),^{
//Update UI in this block of code
});

Call same function from two different threads

I have a function:
- (void)fetchClassListOnCompletion:(void(^) (BOOL success, NSArray *classlist))completionBlock;
I want to call this function from two different thread (may be simultaneously) and want the classlist from the one that finished last.
Need help to implement this
Try this:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
[self fetchClassListOnCompletion:^(BOOL success, NSArray *classlist) {
self.classList = classlist;
}];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
[self fetchClassListOnCompletion:^(BOOL success, NSArray *classlist) {
self.classList = classlist;
}];
});
self.classList will be assigned to classlist from the block that finishes last.
Because this runs on background thread, make sure your method doesn't need to be run on main thread.

Return method only when ready?

I have a method in which I run a couple of other methods. These have completion blocks, I only want to return a value at the end of my main method once I have a result from each of my sub methods. Example:
-(NSMutableDictionary *)mainMethod
{
[self subMethod1Complete:^(NSMutableArray *results)
{
}
[self subMethod2Complete:^(NSMutableArray *results)
{
}
//return...
}
I only want to return my dictionary at the end once the two sub method have completed. How can I do this?
I did have the idea of storing a BOOL for each method, so I know, NO incomplete and YES complete. So when both are YES, I return my dict. But how I can call it on time and not prematurely?
Update
I have tweaked my code to use a completion block, so when I finally receive the data from two other completion blocks from other methods, I run the final one with compiled results. Below you can see my method. You can see my method below, no success thus far, the final completion block is still getting called prematurely.
Important bits for me. getTitles and getThumbnails methods. In the completion block of these I get the data I need. Only when I have both of these, do I want to call my final completion block of this main method. As a result, it will pass on both titles and thumbnails once they have been received.
-(void)getFeedForUserID:(NSString *)channelID delegate:(id<YTHelperDelegate>)delegate complete:(void (^)(NSMutableDictionary * result))completionBlock properties:(NSString *)element, ...
{
va_list args;
va_start(args, element);
NSMutableArray *array = [NSMutableArray new];
for (NSString *arg = element; arg != nil; arg = va_arg(args, NSString *)) [array addObject:arg];
va_end(args);
NSMutableDictionary *resultsDict = [NSMutableDictionary new];
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_group_t group = dispatch_group_create();
for (NSString *string in array)
{
if ([string isEqualToString:kFeedElementTitle])
{
dispatch_group_async(group, queue, ^{
[self getTitlesArrayForChannel:channelID completionHandler:^(NSMutableArray *results) {
dispatch_group_async(group, dispatch_get_main_queue(), ^{
[resultsDict setObject:results forKey:kFeedElementTitle];
});
}];
});
}
if ([string isEqualToString:kFeedElementTitle])
{
dispatch_group_async(group, queue, ^{
[self getThumbnailsArrayForChannel:channelID completionHandler:^(NSMutableArray *results) {
dispatch_group_async(group, dispatch_get_main_queue(), ^{
[resultsDict setObject:results forKey:kFeedElementThumbnail];
});
}];
});
}
}
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
completionBlock(resultsDict);
});
}
You can use GCD and the dispatch groups feature. Here's an article that explains it: http://www.objc.io/issue-2/low-level-concurrency-apis.html#groups
For example in your case, your code might look something like this (shamelessly copied from the article and adapted a bit)...
- (void)asyncMethod {
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_group_async(group, queue, ^(){
NSMutableArray * results = [self subMethod1];
dispatch_group_async(group, dispatch_get_main_queue(), ^(){
self.subMethod1Results = results;
});
});
dispatch_group_async(group, queue, ^(){
NSMutableArray * results = [self subMethod2];
dispatch_group_async(group, dispatch_get_main_queue(), ^(){
self.subMethod2Results = results;
});
});
// This block will run once everything above is done:
dispatch_group_notify(group, dispatch_get_main_queue(), ^(){
// notify the app that both sets of data are ready
[self notifyWorkIsDone];
// and release the dispatch group
dispatch_release(group);
});
}
This requires a little modification to how your class works, because the above method is asynchronous (which is a good thing--it's not going to block your app while all that work is being done). All you need is some sort of handler to call and notify your app that your data is ready and you can update your UI or do whatever additional processing is necessary.
You are looking for GCD's dispatch_group APIs. Here is some sample code from Apple's Concurrency Programming Guide:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
// Add a task to the group
dispatch_group_async(group, queue, ^{
// Some asynchronous work
});
// Do some other work while the tasks execute.
// When you cannot make any more forward progress,
// wait on the group to block the current thread.
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
// Release the group when it is no longer needed.
dispatch_release(group);
Comments on updated code:
Are you sure your mistake is not that your second if statement checks kFeedElementTitle a second time instead of kFeedElementThumbnail which I think may be what you intended?
Updated with working example:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSString *kFeedElementTitle = #"some";
NSString *kFeedElementThumbnail = #"strings";
NSArray *array = #[#"some", #"test", #"strings"];
NSMutableDictionary *resultsDict = [NSMutableDictionary new];
NSLog(#"App launched");
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
for (NSString *string in array)
{
if ([string isEqualToString:kFeedElementTitle])
{
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:5]; // simulate network call
dispatch_group_async(group, dispatch_get_main_queue(), ^{
[resultsDict setObject:#"title result" forKey:kFeedElementTitle];
NSLog(#"Received title result");
});
});
}
if ([string isEqualToString:kFeedElementThumbnail]) // Note: this was changed to kFeedElementThumbnail from kFeedElementTitle
{
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:10]; // simulate network call
dispatch_group_async(group, dispatch_get_main_queue(), ^{
[resultsDict setObject:#"thumbnail result" forKey:kFeedElementThumbnail];
NSLog(#"Received thumbnail result");
});
});
}
}
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(#"final dictionary: %#", resultsDict);
});
return YES;
}
Output:
2013-07-16 21:02:46.468 d[947:a0b] App launched
2013-07-16 21:02:51.471 d[947:a0b] Received title result
2013-07-16 21:02:56.471 d[947:a0b] Received thumbnail result
2013-07-16 21:02:56.472 d[947:a0b] final dictionary: {
some = "title result";
strings = "thumbnail result";
}
you do not know when the blocks are going to return so you will not know if you have the data at the time, if i may make a suggestion you call a method with in those blocks that method will check to see if both dictionaries are set and if they are then continue with the process otherwise don't continue
- (void)mainMethod
{
[self subMethod1Complete:^(NSMutableArray *results)
{
self.result1 = results;
[self method3];
}
[self subMethod2Complete:^(NSMutableArray *results)
{
self.results2 = results;
[self method3];
}
}
- (void)method3 {
if ( self.results1 != nil && self.results2 != nil ) {
[self startProcedure];
} else {
// do nothing
}
}
although all together i would suggest reworking your code to do this differently, simply because you can't guarantee that one of the blocks will be done by the time of the return, let alone both of them
you can also do something like this
-(NSMutableDictionary *)mainMethod
{
[self subMethod1Complete:^(NSMutableArray *results)
{
}
[self subMethod2Complete:^(NSMutableArray *results)
{
}
while(result == nil)
sleep(1);
//return...
}
which again is really bad.... it's just better to re-write the code

iOS: How do I get my blocks to execute without interrupting each other?

I have a process that I'm trying to create where I download different xml files and parse them from a website. At the moment I can get them to all work by using...
dispatch_async(dispatch_get_current_queue(),^ { [self updatePersons]; });
dispatch_async(dispatch_get_current_queue(),^ { [self updatePlaces]; });
dispatch_async(dispatch_get_current_queue(),^ { [self updatePositions]; });
dispatch_async(dispatch_get_current_queue(),^ { [self updateContacts]; });
This locks up the main thread because I'm using dispatch_get_current_queue. How can I get these to execute without interrupting each other which seems to be the case when trying the method below.
dispatch_async(dispatch_get_global_queue(0,0),^ { [self updatePersons]; });
The full version of my code I'm using is below, but what happens according to my Log output is they execute at the same time (which I get is kinda the purpose if I understand everything right); however, the code for them never really finishes... I cannot tell if everything is getting parsed or not through the Log Output and the main screen stays frozen. The log only shows part of the logs it should be showing... seems to be interrupted by the other process's NSLog messages.
dispatch_async(dispatch_get_global_queue(0,0),^ { [self updatePersons]; });
dispatch_async(dispatch_get_global_queue(0,0),^ { [self updatePlaces]; });
dispatch_async(dispatch_get_global_queue(0,0),^ { [self updatePositions]; });
dispatch_async(dispatch_get_global_queue(0,0),^ { [self updateContacts]; });
dispatch_async(dispatch_get_global_queue(-2,0),^ {
dispatch_async(dispatch_get_main_queue(),^ {[self setBottomBarToUpdated]; });
});
I'm trying to create a way for the app to pull down the information through each of the functions above, while updating the user on the progress while it's executing by a label on the bottom bar. I have the updating the text working fine it seems by using the below code in each of the four functions...
dispatch_async(dispatch_get_main_queue(),^ {
[self setBottomBarToUpdating:#"Updating Places..."];
});
If anyone can offer help to the proper way to use the queues so my app doesn't lock up I'd greatly appreciate it. Thanks!
If you create you own private concurrent dispatch queue, then you can use a "barrier block". This block will be executed only when all other previously submitted blocks are finished.
dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);
// These blocks are executed concurrently:
dispatch_async(queue, ^ { [self updatePersons]; });
dispatch_async(queue, ^ { [self updatePlaces]; });
// ...
dispatch_barrier_async(queue, ^ {
// This will be executed when all previous blocks are finished.
dispatch_async(dispatch_get_main_queue(), ^{
[self setBottomBarToUpdated];
});
});
It sounds like you want them to execute sequentially, updating the UI as they progress. If so, you can use a serial queue:
dispatch_queue_t serialQueue = dispatch_queue_create("com.company.MyQueue", NULL);
dispatch_async(serialQueue,^ {
dispatch_async(dispatch_get_main_queue(), ^{
[self setBottomBarToUpdating:#"Updating Persons..."];
}
[self updatePersons];
});
dispatch_async(serialQueue,^ {
dispatch_async(dispatch_get_main_queue(), ^{
[self setBottomBarToUpdating:#"Updating Places..."];
}
[self updatePlaces];
});
dispatch_async(serialQueue,^ {
dispatch_async(dispatch_get_main_queue(), ^{
[self setBottomBarToUpdating:#"Updating Positions..."];
}
[self updatePositions];
});
dispatch_async(serialQueue,^ {
dispatch_async(dispatch_get_main_queue(), ^{
[self setBottomBarToUpdating:#"Updating Contacts..."];
}
[self updateContacts];
dispatch_async(dispatch_get_main_queue(), ^{
[self setBottomBarToUpdating:#"Finished updating"];
}
});
dispatch_release(serialQueue);

Resources