Using Blocks and GCD to manage tasks - ios

I'm learning iOS and when it comes to GCD, it's confusing.
Let's get it out of the way, I'm writing a small program that fetch data from the internet.
Here is my viewcontroller
NSMutableArray dataArray = [NSMutableArray array];
[querysomethingwithblock:(^ {
//do some stuff here
[otherquerywithblock:( ^ {
//do some stuff here
// Here I got the data from internet
// Do loop action
[dataArray addObject:data];
})];
})];
// here I want to perform some actions only after get data from internet
[self performAction:dataArray];
How can I achieve this purpose. In practical, [self performAction:dataArray] always get fired before I get the data. I tried to play with GCD but no luck.
Here is some patterns I've tried so far
dispatch_async(queue, ^{
// Do query stuff here
dispatch_async(dispatch_get_mainqueue(), ^{
//perform action here
});
{;
Or using dispatch_group_async, dispatch_group_wait, dispatch_group_notify
The only way I can handle right now is to use dispatch_after but the point is the downloading time is variable, it's not good practice to have a specific time here
Thank you so much for any advice.

The part of code called Do query stuff here i assume is async already, why put it inside a dispatch_queue then?
If instead you manage to do a synchronous query, your code (the second snippet) would work, as the dispatch to the main queue would be executed only after the query finished.
If you don't have an option to execute the query in a synchronous manner, then you need some mechanism to register either a block or a callback to be executed when the download is finished.
At the end of the day, it all depends on what kind of query you have in there and what methods it offers for you to register an action to be performed when the download is finished.

Related

Using while(true) statement a valid approach in iOS programming?

In objective C,
I am making my program to wait using while loop
doInitialize()
{
dispach_group_t loadDataGroup=dispatch_group_create();
dispatch_group_async(loadDataGroup,...get_global_queue(..),0),^{
renewauth();
}
dispatch_group_notify(loadDataGroup,...get_global_queue(..),0),^{
//Do other tasks once renew session has completed...
}
}
renewauth()
{
RenewAuthTokenInProgress=true;
startRenewThread();
**while (RenewAuthTokenInProgress);**
}
In turn startRenewThread() function also performs dispatch_async operation inside. So I have to make renewAuth() wait.
And async task in startRenewThread will update the bool variable once renewal is successful.
Is there any better approach of doing it other than dispatch_groups?
And is it good to make other threads wait with while (true) statement?
Manoj Kumar,
using a while loop to wait till the boolean variable change is not the correct approach to solve the problem. Here are few of the issues with this method
Your CPU is un-necessarily burdened with checking the variable regularly.
This will clearly show that developer isn't much equipted with basic skills of coding and features available with language.
If for any reason your variable will never change then your CPU will never stop checking the value of bool in while loop and blocks the execution of further code on the same thread.
Here are few of the correct approach :
Blocks or closures : Make use of blocks to execute the code asynchronously when the RenewAuthToken is done.
Delegates : if blocks are harder to understand, Make use of delegates and trigger the delegate when you are done with RenewAuthToken.
Notifications : Add observer for notifications in classes which needs to respond when RenewAuthToken is done and throw notification from the asynctask and let the class to catch it execute the code.
Locks : If it is necessary to block the execution of the thread till the response comes use locks to control the thread execution rather than using while loop
EDIT
As pointed out by fogmeister in comments
If you block the main thread for too long with a while(true) loop then
the app will actually be terminated by the iOS Watchdog as it will
assume it has crashed
Please have a look at the link : understand iOS watchdog termination reasons provided by fogmeister
Hope it helps.
I believe what you need it's a semaphore like:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
__block BOOL done = FALSE;
while (true) {
[self someCompletionMethod completion:^(BOOL success) {
if(success) { // Stop condition
done = TRUE;
}
// do something
dispatch_semaphore_signal(sem); // This will let a new iteration
}];
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
if(done) {
dispatch_async(dispatch_get_main_queue(), ^{
// Dispatch to main
NSLog(#"Done!");
break;
});
}
}
});
Semaphores are an old-school threading concept introduced to the world by the ever-so-humble Edsger W. Dijkstra. Semaphores are a complex topic because they build upon the intricacies of operating system functions.
You can see a tutorial here about semaphore and check it out more links: https://www.raywenderlich.com/63338/grand-central-dispatch-in-depth-part-2
I hope this can help you.
What you do is absolutely lethal. It blocks the running thread (presumably the main thread) so the UI is frozen. It runs one core at 100% load for no reason whatsoever which empties the battery rapidly and heats up the phone. This will get you some very, very unhappy customers or very, very happy ex-customers.
Anything like this has to run in the background: startRenewThread should trigger some action that sets RenewAuthTokenInProgress = NO and sets whether there is a new token or not, and then triggers further action.
This is an absolutely essential programming pattern on iOS (and Android as far as I know).

Heavy calculations on a background thread in getter

I need to perform a lot of calculations every time a getter is called from my app. The data returned from the getter is constantly changing based on the environment, and it has to do a lot of calculations to compute what it should return. Therefore, I don't want the code in the getter running on the main thread. This is what I have so far:
#interface Calculator ()
#property (nonatomic, strong) dispatch_queue_t calculationThread;
#end
- (dispatch_queue_t)calculationThread {
if (!_calculationThread) {
_calculationThread = dispatch_queue_create("calculation_thread", NULL);
}
return _calculationThread;
}
- (NSArray *)calculation {
// perform calculation in calculationThread, which should not be on main thread and be asynchronous
return arrayContainingCalculations;
}
I basically want to know how to use GCD to replace the comment. I have tried using dispatch_queue_t and dispatch_group_notify, but I don't seem to be implementing it correctly.
I think using a callback is probably the simplest and most efficient solution to this problem.
It is simply impossible to use only a single getter to do an asynchronous calculation without blocking the thread it was called on, as you expect code called after it to continue executing while it does the calculation.
You just have to create a new method with a callback, for example:
-(void) doCalculation:(void(^)(NSArray* result))callback {
dispatch_async(self.calculationQueue, ^{
NSArray* result = self.calculation; // make sure this is doing a synchronous calculation. If it's asynchronous, you'll have to use a semaphore (or another callback!).
if (callback) {
dispatch_async(dispatch_get_main_queue(), ^{ // return to main thread
callback(result);
});
}
});
}
Then you can simply invoke it on your main thread like so:
[calculator doCalculation:^(NSArray* result) {
textView.text = [result[0] stringValue]; // update UI with new info.
}];
That way you can easily keep your resulting code in-line with the call to the method.
It's also worth noting that your calculationQueue's getter (I renamed it, as the word thread is misleading when you're working with queues) isn't thread-safe. I would advise you use a dispatch_once to make it thread-safe:
-(dispatch_queue_t) calculationQueue {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_calculationQueue = dispatch_queue_create("calculation_queue", DISPATCH_QUEUE_SERIAL);
});
return _calculationQueue;
}
You can use the following to put it on your queue asynchronously. The problem however is that the method is going to return immediately.
dispatch_async(your_queue, ^{
// Code to be executed on background thread
});
What you probably want is to have some kind of method calculateWithCompletion where the caller can define a block that you can invoke once the completion is finished.
As you said in your comment to Peter, you want to keep it so you can call self.calculation and get your logic executed and return the calculation synchronously.
However because you want to avoid locking the UI while this logic is executing, you would like it to execute on a background thread.
Therefore, all you should need to do is use dispatch_sync instead of dispatch_async inside of your calculate method.
What dispatch_sync does is it places a task (the block that contains your logic) onto a specified queue (probably should pick a global concurrent queue), which then executes your task on a thread the OS picks for you (not the main thread). dispatch_async does the same, Except that dispatch_async will continue execution immediately after dispatching your task onto a queue.
dispatch_sync on the other hand, will block execution in the current run loop until your tasks returns.
This will allow you to execute your expensive logic on a background thread, while still remaining synchronous so that you can continue using self.calculation

GCD serial queue does not seem to execute serially

I have a method that at times can be invoked throughout my code. Below is a very basic example, as the code processes images and files off of the iphone photo gallery and marks them already processed when done with the method.
#property (nonatomic, assign) dispatch_queue_t serialQueue;
....
-(void)processImages
{
dispatch_async(self.serialQueue, ^{
//block to process images
NSLog(#"In processImages");
....
NSLog(#"Done with processImages");
});
}
I would think that each time this method is called I would get the below output...
"In processImages"
"Done with processImages"
"In processImages"
"Done with processImages"
etc...
but I always get
"In processImages"
"In processImages"
"Done with processImages"
"Done with processImages"
etc...
I thought a serial queue would wait till the first block is done, then start. To me it seems it is starting the method, then it gets called again and starts up before the first call even finishes, creating duplicates of images that normally would not be processed due to the fact that if it really executed serially the method would know they were already processed. Maybe my understanding of serial queues is not concrete. Any input? Thank you.
EDIT:MORE Context below, this is what is going on in the block...Could this cause the issue???
#property (nonatomic, assign) dispatch_queue_t serialQueue;
....
-(void)processImages
{
dispatch_async(self.serialQueue, ^{
//library is a reference to ALAssetsLibrary object
[library enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos usingBlock:^(ALAssetsGroup *group, BOOL *stop)
{
[group enumerateAssetsUsingBlock:^(ALAsset *asset, NSUInteger index, BOOL *stop)
{
....
//Process the photos here
}];
failureBlock:^(NSError *error) { NSLog(#"Error loading images from library");
}];
});
}
-(id)init
{
self = [super init];
if(self)
{
_serialQueue = dispatch_queue_create("com.image.queue",NULL);
}
return self;
}
this object is only created once, and as far as I can tell can never be created again based off my code...I will run tests to make sure though.
UPDATE 2: WHAT I THINK IS HAPPENING, please comment on this if you agree/disagree....
Obviously my main issue is that it seems this block of code is being executed concurrently, creating duplicate entries (importing the same photo twice) when it wouldn't normally do this if it was run serially. When a photo is processed a "dirty" bit is applied to it ensuring the next time the method is invoked it skips this image, but this is not happening and some images are processed twice. Could this be due to the fact I am enumerating the objects in a second queue using enumerategroupswithtypes: within that serialQueue?
call processImages
enumerateObjects
immediately return from enumerateObjects since it is async itself
end call to processImages
processImages is not really done though due to the fact that enumerategroups is probably still running but the queue might thing it is done since it reaches the end of the block before enumerategroups is finished working. This seems like a possibility to me?
Serial Queues ABSOLUTELY will perform serially. They are not guaranteed to perform on the same thread however.
Assuming you are using the same serial queue, the problems is that NSLog is NOT guaranteed to output results in the proper order when called near simultaneously from different threads.
here is an example:
SQ runs on thread X, sends "In processImages"
log prints "In proc"
SQ on thread X, sends "Done with processImages"
SQ runs on thread Y, sends "In processImages"
log prints "essImages\n"
After 5., NSLog doesn't necessarily know which to print, 3. or 4.
If you absolutely need time ordered logging, You need a dedicated queue for logging. In practice, I've had no problems with just using the main queue:
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"whatever");
});
If all NSlog calls are the on the same queue, you shouldn't have this problem.
enumerateGroupsWithTypes:usingBlock:failureBlock: does its work asynchronously on another thread and calls the blocks passed in when it's done (on the main thread I think). Looking at it from another perspective, if it completed all the synchronously by the time the method call was complete, it could just return an enumerator object of the groups instead, for instance, for a simpler API.
From the documentation:
This method is asynchronous. When groups are enumerated, the user may be asked to confirm the application's access to the data; the method, though, returns immediately. You should perform whatever work you want with the assets in enumerationBlock.
I'm not sure why you're trying to accomplish by using the serial queue, but if you just want to prevent simultaneous access, then you could just add a variable somewhere that keeps track of whether we're currently enumerating or not and check that at first, if you don't have to worry about synchronization issues. (If you do, perhaps you should look into using a GCD group, but it's probably overkill for this situation.)
If the question is "Can serial queue perform tasks asynchronously?" then the answer is no.
If you think that it can, you should make sure that all tasks are really performing on the same queue. You can add the following line in the block and compare the output:
dispatch_async(self.serialQueue, ^{
NSLog(#"current queue:%p current thread:%#",dispatch_get_current_queue(),[NSThread currentThread]);
Make sure that you write NSLog in the block that performs on your queue and not in the enumerateGroupsWithTypes:usingBlock:failureBlock:
Also you can try to create your queue like this
dispatch_queue_create("label", DISPATCH_QUEUE_SERIAL);
but I don't think that will change anything
EDIT:
By the way, method
enumerateGroupsWithTypes:usingBlock:failureBlock:
is asynchronous, why do you call it on another queue?
UPDATE 2:
I can suggest something like this:
dispatch_async(queue, ^{
NSLog(#"queue");
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER, *pmutex = &mutex;
pthread_mutex_lock(pmutex);
ALAssetsLibraryGroupsEnumerationResultsBlock listGroupBlock = ^(ALAssetsGroup *group, BOOL *stop) {
NSLog(#"block");
if (group) {
[groups addObject:group];
} else {
[self.tableView performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:NO];
dispatch_async(dispatch_get_current_queue(), ^{
pthread_mutex_unlock(pmutex);
});
}
NSLog(#"block end");
};
[assetsLibrary enumerateGroupsWithTypes:groupTypes usingBlock:listGroupBlock failureBlock:failureBlock];
pthread_mutex_lock(pmutex);
pthread_mutex_unlock(pmutex);
pthread_mutex_destroy(pmutex);
NSLog(#"queue end");
});
I hit an issue like this, and the answer for me was to realize that asynchronous calls from a method on the serialized queue goes to another queue for processing -- one that is not serialized.
So you have to wrap all the calls inside the main method with explicit dispatch_async(serializedQueue, ^{}) to ensure that everything is done in the correct order...
Using Swift and semaphores to illustrate an approach to serialization:
Given: a class with an asynchronous ‘run’ method that will be run on multiple objects at once, and the objective is that each not run until the one before it completes.
The issue is that the run method allocates a lot of memory and uses a lot of system resources that can cause memory pressure among other issues if too many are run at once.
So the idea is: if a serial queue is used then only one will run at a time, one after the other.
Create a serial queue in the global space by the class:
let serialGeneratorQueue: DispatchQueue = DispatchQueue(label: "com.limit-point.serialGeneratorQueue", autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency.workItem)
class Generator {
func run() {
asynchronous_method()
}
func start() {
serialGeneratorQueue.async {
self.run()
}
}
func completed() {
// to be called by the asynchronous_method() when done
}
}
The ‘run’ method of this class for which very many objects will be created and run will be processed on the serial queue:
serialGeneratorQueue.async {
self.run()
}
In this case an autoreleaseFrequency is .workItem to clean up memory after each run.
The run method is of some general form:
func run() {
asynchronous_method()
}
The problem with this: the run method exits before the asynchronous_method completes, and the next run method in the queue will run, etc. So the objective is not being achieved because each asynchronous_method is running in parallel, not serially after all.
Use a semaphore to fix. In the class declare
let running = DispatchSemaphore(value: 0)
Now the asynchronous_method completes it calls the ‘completed’ method:
func completed() {
// some cleanup work etc.
}
The semaphore can be used to serialized the chain of asynchronous_method’s by add ‘running.wait()’ to the ‘run’ method:
func run() {
asynchronous_method()
running.wait()
}
And then in the completed() method add ‘running.signal()’
func completed() {
// some cleanup work etc.
running.signal()
}
The running.wait() in ‘run’ will prevent it from exiting until signaled by the completed method using running.signal(), which in turn prevents the serial queue from starting the next run method in the queue. This way the chain of asynchronous methods will indeed be run serially.
So now the class is of the form:
class Generator {
let running = DispatchSemaphore(value: 0)
func run() {
asynchronous_method()
running.wait()
}
func start() {
serialGeneratorQueue.async {
self.run()
}
}
func completed() {
// to be called by the asynchronous_method() when done
running.signal()
}
}
I thought a serial queue would wait [until] the first block is done ...
It does. But your first block simply calls enumerateGroupsWithTypes and the documentation warns us that the method runs asynchronously:
This method is asynchronous. When groups are enumerated, the user may be asked to confirm the application's access to the data; the method, though, returns immediately.
(FWIW, whenever you see a method that has a block/closure parameter, that’s a red flag that the method is likely performing something asynchronously. You can always refer to the relevant method’s documentation and confirm, like we have here.)
So, bottom line, your queue is serial, but it is only sequentially launching a series of asynchronous tasks, but obviously not waiting for those asynchronous tasks to finish, defeating the intent of the serial queue.
So, if you really need to have each tasks wait for the prior asynchronous task, there are a number of traditional solutions to this problem:
Use recursive pattern. I.e., write a rendition of processImage that takes an array of images to process and:
check to see if there are any images to process;
process first image; and
when done (i.e. in the completion handler block), remove the first image from the array and then call processImage again.
Rather than dispatch queues, consider using operation queues. Then you can implement your task as an “asynchronous” NSOperation subclass. This is a very elegant way of wrapping an asynchronous task This is illustrated in https://stackoverflow.com/a/21205992/1271826.
You can use semaphores to make this asynchronous task behave synchronously. This is also illustrated in https://stackoverflow.com/a/21205992/1271826.
Option 1 is the simplest, option 2 is the most elegant, and option 3 is a fragile solution that should be avoided if you can.
You might have more than one object, each with its own serial queue. Tasks dispatched to any single serial queue are performed serially, but tasks dispatched to different serial queues will absolutely be interleaved.
Another simple bug would be to create not a serial queue, but a concurrent queue...

Waiting on Multiple Async Tasks to Complete

I like to show a loading message when the app is fetching news and videos. Currently, I have the following code:
[MBProgressHUD showHUDAddedTo:self.view animated:YES];
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
[self loadVersion];
[self loadFeaturedNews];
[self loadFeaturedVideo];
dispatch_async(dispatch_get_main_queue(), ^{
[MBProgressHUD hideHUDForView:self.view animated:YES];
[self dismissViewControllerAnimated:NO completion:nil];
});
});
I would like to dismiss the controller and hide the progress view only when all the tasks (news, videos) are loaded.
If you're using AFNetworking, you might want to look at enqueueBatchOfHTTPRequestOperations. I'd refer you to the AFNetworking FAQ:
How can I wait for a group of requests to finish before processing them?
Use [enqueueBatchOfHTTPRequestOperationsWithRequests] or [enqueueBatchOfHTTPRequestOperations] to batch a series of requests together, specifying a callback for when all of the requests have finished. As mentioned in the question about waiting for completion blocks to finish, you may not want to set completion blocks on each individual operation, and instead access the response object properties directly in the batch completion block.
I gather that loadVersion, loadFeaturedNews, and loadFeaturedVideo methods are each asynchronously downloading content. If you were using NSURLConnection, I would suggest changing them to operate synchronously. The specifics of the solution will vary depending upon how you're currently downloading stuff in those routines e.g. if you're using NSURLConnection method, initWithRequest, you could use sendSynchronousRequest.
Personally, I'd be inclined to combine that with doing the requests concurrently with dispatch groups or NSOperationQueue dependencies, but first focus on getting them to run synchronously. I'd actually use NSOperationQueue so I could easily cap the number of concurrent operations to four, or something reasonable like that.
By the way, I'm not suggesting you change the code in your question at all. Keep that dispatch_async. I'm suggesting you fix the loadVersion, loadFeaturedNews, and loadFeaturedVideo methods, themselves, to operate synchronously.
Then use completion handler for this. in that you can write your code which is to be executed after completion
completion:^ (BOOL finished) {
}

How to lazy/async load various elements(data in labels) in a View?

I have a ViewController with various labels. Each of these labels get dynamically populated at the run time based on various regex parsing logic running on an html page. The problem is, each regex match is taking 2-3 seconds and I have 8 such labels so that means i have to wait somewhere around 20-25 seconds before the view shows up!
This is a very very bad user experience. I want that to make this less painful for the user and therefore want to load each label independently as and when they get the data after the regex is processed and not wait for all 8 labels to finish retrieving their regex matches.
Any way this can be achieved in ios 5?
Create a separate function which calculates the values that you need.
(You probably already have this anyway for code readability/maintainability.)
Run this thread in a background thread.
When you are ready to actually set the text, make sure that you do it on the main thread:
Here is an example:
- (void)calculateLabelText {
NSString *label1Text = // However you calculate this...
dispatch_async(dispatch_get_main_queue(), ^(void) {
self.label1.text = label1Text;
});
NSString *label2Text = // However you calculate this...
dispatch_async(dispatch_get_main_queue(), ^(void) {
self.label2.text = label2Text;
});
}
In viewDidLoad, add this:
[self performSelectorInBackground:#selector(calculateLabelText) withObject:nil];
Use Grand Central Dispatch (GCD). It'll handle queues and threading etc for you. There's no need to create a method just for one set of operations that happens once, and regardless, dispatch_async() is faster than performing a selector on a background thread and you get to keep your existing code structure - you just wrap it up in a nice concurrent bundle that won't block the main thread :)
// get a reference to the global concurrent queue (no need to create our own).
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
for each label's regex operation {
dispatch_async(queue, ^{
// your regex here for one
// execute back in the main thread since UIKit only operates in the main thread.
dispatch_async(dispatch_get_main_queue(), ^{
[myLabel setText:<result of operations>];
});
});
}
if you want to use this, you have to pay some attention to get your code separated. one part do data loading work, the other one set data to controls. and you have to make sure that you sqlite (i assume u used this db) cool with multi thread.

Resources