Updating the UI when using a serial queue - iphone

I am using a serial queue to do a background thread (block) for video processing. I want to update a UI component (specifically a progress bar). I've found that while I can interact with the UI, my progress bar is not updating with calls to setProgress (called from the block), until the thread has finished.
dispatch_queue_t dispatch_queue = dispatch_queue_create("somequeue", NULL);
[somebody doSomethingOnQueue:dispatch_queue usingBlock:^{
progressBar.progress = someFloat; //does not update
}];

You should update the UI on the main dispatch queue:
[somebody doSomethingOnQueue:dispatch_queue usingBlock:^{
…
dispatch_async(dispatch_get_main_queue(), ^{
progressBar.progress = someFloat;
});
}];

You can use performSelectorOnMainThread:withObject:waitUntilDone:.

Related

Why dispatch_sync( ) call on main queue is blocking the main queue?

I know this is not a strong question but I have to clear my mind on this concept.
I have defined myBlock as follows.
void(^myBlock)(void) = ^{
for(int i = 0;i < 10 ; i++)
{
NSLog(#"%d and current queue = %#",i,[NSThread currentThread]);
}
};
Now In viewDidLoad method when I uses the dispatch_sync() method independently on main queue then the main queue gets blocked.
Here is the Sample.
- (void)viewDidLoad
{
[super viewDidLoad];
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue,myBlock);
}
But But, when I use the same dispatch_sync() function on main thread Inside a block of dispatch_async() function which is fired on concurrent queue then the main thread does not blocked.
Here is the sample.
- (void)viewDidLoad
{
[super viewDidLoad];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue,^{
dispatch_sync(dispatch_get_main_queue(),myBlock);
});
}
I am not clear why this is happening? Why main thread blocked when calling dispatch_sync() independently?
When using dispatch_sync on a serial queue (like the main queue) the current thread has to wait until the dispatched code is executed.
A dead lock occurs when a block is dispatched synchronously on from a serial queue to the same queue.
There is only one main queue. In your first example, viewDidLoad is running on it. You then tell viewDidLoad to wait (i.e. "sync") on something else that's going to run on the main queue. They both can't be on it at exactly the same time.
In your second example, it's the concurrent queue that's being told to wait. That's not a problem because by doing dispatch_async, viewWillLoad is giving up the main queue and making it available for your block to run.
Dispatching a block on main queue is equivalent of calling it on the main thread. Main queue executes on main thread.
Since you are dispatching using dispatch_sync this will be a blocking call as dispatch_sync,
Submits a block object for execution on a dispatch queue and waits until that block completes.
Just need to understand this:
dispatch_sync() blocks the dispatch queue, submit the block to it and
waits until the submitted block completes.
dispatch_async() submits the block for asynchronous execution on dispatch queue and returns immediately.
When you run an async task, it will create a new thread and your code in the block will be executed in that new thread. In that method you call dispatch_sync on main thread because you want to run in the main queue. Please try to understand it with this example.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
if ([NSThread isMainThread])
{
NSLog(#"Running on main Thread in dispatch_async");
}
else
{
NSLog(#"Running on another Thread in dispatch_async");
}
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
if ([NSThread isMainThread])
{
NSLog(#"Running on main Thread in dispatch_sync");
}
else
{
NSLog(#"Running on another Thread in dispatch_sync");
}
});
dispatch_sync(dispatch_get_main_queue(), ^(void) {
if ([NSThread isMainThread])
{
NSLog(#"Running on main Thread in dispatch_sync");
}
else
{
NSLog(#"Running on another Thread in dispatch_sync");
}
});
});
Output is:
Running on another Thread in dispatch_async
Running on another Thread in dispatch_sync
Running on main Thread in dispatch_sync

how to wait until method containing block get executed

I am in the situation where i need to wait until method get executed and then only execution move ahead i have tried cfrunlooprun and
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_async(queue, ^{
[eventStore requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted, NSError *error)
{
but this did not helped me my code is below
- (NSArray*) calendarMonthView:(TKCalendarMonthView*)monthView marksFromDate:(NSDate*)startDate
toDate:(NSDate*)lastDate{
[self fatchAllEvent];// this method contain block it takes 1 second to get executed and events array get filled from that block
NSLog(#"all event %#",events); it shows null here cuse blocked has not executed yet
[self generateRandomDataForStartDate:startDate endDate:lastDate]; this method need events array but as there is no data so nothing happens
return self.dataArray;
}
how can i wait until fatchAllEvent method get executed and then only after execution processed
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, ^{
[self method1:a];
});
// Add another task to the group
dispatch_group_async(group, queue, ^{
[self method2:a];
});
// Add a handler function for when the entire group completes
// It's possible that this will happen immediately if the other methods have already finished
dispatch_group_notify(group, queue, ^{
[self methodFinish]
});
Dispatch groups are ARC managed. They are retained by the system until all of their blocks run, so their memory management is easy under ARC.
See also dispatch_group_wait() if you want to block execution until the group finishes.

Perfoming UI updates in secondary queue

I am using the new CoreMotion framework to monitor some of the hardware devices. Here is the typical code to do that:
-(void)startAccelerometer{
self.motion.accelerometerUpdateInterval = 1/30.0f;
NSOperationQueue* accelerometerQueue = [[NSOperationQueue alloc] init];
CMAccelerometerHandler accelerometerHandler = ^(CMAccelerometerData *accelerometerData, NSError *error) {
NSLog(#"Accelerometer realtime values");
NSLog(#"x=%f", accelerometerData.acceleration.x);
NSLog(#"y=%f", accelerometerData.acceleration.y);
NSLog(#"z=%f", accelerometerData.acceleration.z);
NSLog(#" ");
};
[self.motion startAccelerometerUpdatesToQueue:accelerometerQueue withHandler:[[accelerometerHandler copy]autorelease]];
}
That works just fine. Now I want to print the values on a UILabel, but since the CoreMotion frameworks has you use blocks, this is not guaranteed to be in the main queue (and in fact isn't for my app). Is it is "wrong" to just run the label's setter on the main queue like this?
-(void)startAccelerometer{
self.motion.accelerometerUpdateInterval = 1/30.0f;
NSOperationQueue* accelerometerQueue = [[NSOperationQueue alloc] init];
CMAccelerometerHandler accelerometerHandler = ^(CMAccelerometerData *accelerometerData, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
self.lblAccelerometer.text = [NSString stringWithFormat:#"Accelerometer:\nx = %f\ny = %f\nz = %f",
accelerometerData.acceleration.x,
accelerometerData.acceleration.y,
accelerometerData.acceleration.z];
});
};
[self.motion startAccelerometerUpdatesToQueue:accelerometerQueue withHandler:[[accelerometerHandler copy]autorelease]];
}
It works just fine and I don't really see any reason why this would be frowned upon. Any thoughts on that?
This is a common method that I use in many projects. UI updates must occur on the main thread.
//Dispatch on background thread
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//background processing goes here
//Dispatch on main thread
dispatch_async(dispatch_get_main_queue(), ^{
//update UI here
});
});
In your case, your UI updates are occurring on the main thread. So I wouldn't worry about changing anything.
You are missunderstanding the concept of blocks, to put it simple:
Blocks are small pieces of code that can be handled as variables and be executed at a certain time or thread.
All UI updates MUST be performed on the main thread so as long as you do this it will be fine.
Codes can be executed in different threads with different priorities in sync or async mode. On your code you are doing it perfectly fine, you not only dispatch it to the Main Queue which is where uiupdates should be executed, but you are also dispatching it async which is the safest way to update send to the main queue (from your code i cannot tell if you are running this specific piece of code from the main queue or a secondary queue but if u were to dispatch a sync block from the main queue to the main queue your program would stop working)
For iOS documentation:
Use the dispatch_get_main_queue function to get the serial dispatch
queue associated with your application’s main thread. This queue is
created automatically for Cocoa applications and for applications that
either call the dispatch_main function or configure a run loop (using
either the CFRunLoopRef type or an NSRunLoop object) on the main
thread.
Read this here http://developer.apple.com/library/ios/#documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html#//apple_ref/doc/uid/TP40008091-CH102-SW1

Updating UI components from an async callback (dispatch_queue)

how can i update GUI elements with values from a queue?
if i use async queue construct, textlable don't get updated.
Here is a code example i use:
- (IBAction)dbSizeButton:(id)sender {
dispatch_queue_t getDbSize = dispatch_queue_create("getDbSize", NULL);
dispatch_async(getDbSize, ^(void)
{
[_dbsizeLable setText:[dbmanager getDbSize]];
});
dispatch_release(getDbSize);
}
Thank you.
As #MarkGranoff said, all UI needs to be handled on the main thread. You could do it with performSelectorOnMainThread, but with GCD it would be something like this:
- (IBAction)dbSizeButton:(id)sender {
dispatch_queue_t getDbSize = dispatch_queue_create("getDbSize", NULL);
dispatch_queue_t main = dispatch_get_main_queue();
dispatch_async(getDbSize, ^(void)
{
dispatch_async(main, ^{
[_dbsizeLable setText:[dbmanager getDbSize]];
});
});
// release
}
Any UI update must be performed on the main thread. So your code would need to modified to use the main dispatch queue, not a queue of your own creation. Or, any of the performSelectorOnMainThread methods would work as well. (But GCD is the way to go, these days!)

GCD, Threads, Program Flow and UI Updating

I'm having a hard time figuring out how to put this all together.
I have a puzzle solving app on the mac.
You enter the puzzle, press a button, and while it's trying to find the number of solutions,
min moves and such I would like to keep the UI updated.
Then once it's finished calculating, re-enable the button and change the title.
Below is some sample code from the button selector, and the solving function:
( Please keep in mind I copy/paste from Xcode so there might be some missing {} or
some other typos.. but it should give you an idea what I'm trying to do.
Basicly, user presses a button, that button is ENABLED=NO, Function called to calculate puzzle. While it's calculating, keep the UI Labels updated with moves/solution data.
Then once it's finished calculating the puzzle, Button is ENABLED=YES;
Called when button is pressed:
- (void) solvePuzzle:(id)sender{
solveButton.enabled = NO;
solveButton.title = #"Working . . . .";
// I've tried using this as a Background thread, but I can't get the code to waitTilDone before continuing and changing the button state.
[self performSelectorInBackground:#selector(createTreeFromNode:) withObject:rootNode];
// I've tried to use GCD but similar issue and can't get UI updated.
//dispatch_queue_t queue = dispatch_queue_create("com.gamesbychris.createTree", 0);
//dispatch_sync(queue, ^{[self createTreeFromNode:rootNode];});
}
// Need to wait here until createTreeFromNode is finished.
solveButton.enabled=YES;
if (numSolutions == 0) {
solveButton.title = #"Not Solvable";
} else {
solveButton.title = #"Solve Puzzle";
}
}
Needs to run in background so UI can be updated:
-(void)createTreeFromNode:(TreeNode *)node
{
// Tried using GCD
dispatch_queue_t main_queue = dispatch_get_main_queue();
...Create Tree Node and find Children Code...
if (!solutionFound){
// Solution not found yet so check other children by recursion.
[self createTreeFromNode:newChild];
} else {
// Solution found.
numSolutions ++;
if (maxMoves < newChild.numberOfMoves) {
maxMoves = newChild.numberOfMoves;
}
if (minMoves < 1 || minMoves > newChild.numberOfMoves) {
solutionNode = newChild;
minMoves = newChild.numberOfMoves;
// Update UI on main Thread
dispatch_async(main_queue, ^{
minMovesLabel.stringValue = [NSString stringWithFormat:#"%d",minMoves];
numSolutionsLabel.stringValue = [NSString stringWithFormat:#"%d",numSolutions];
maxMovesLabel.stringValue = [NSString stringWithFormat:#"%d",maxMoves];
});
}
GCD and performSelectorInBackground samples below. But first, let's look at your code.
You cannot wait where you want to in the code above.
Here's the code you had. Where you say wait in the comment is incorrect. See where I added NO.
- (void) solvePuzzle:(id)sender{
solveButton.enabled = NO;
solveButton.title = #"Working . . . .";
// I've tried using this as a Background thread, but I can't get the code to waitTilDone before continuing and changing the button state.
[self performSelectorInBackground:#selector(createTreeFromNode:) withObject:rootNode];
// NO - do not wait or enable here.
// Need to wait here until createTreeFromNode is finished.
solveButton.enabled=YES;
}
A UI message loop is running on the main thread which keeps the UI running. solvePuzzle is getting called on the main thread so you can't wait - it will block the UI. It also can't set the button back to enabled - the work hasn't been done yet.
It is the worker function's job on the background thread to do the work and then when it's done to then update the UI. But you cannot update the UI from a background thread. If you're not using blocks and using performSelectInBackground, then when you're done, call performSelectorOnMainThread which calls a selector to update your UI.
performSelectorInBackground Sample:
In this snippet, I have a button which invokes the long running work, a status label, and I added a slider to show I can move the slider while the bg work is done.
// on click of button
- (IBAction)doWork:(id)sender
{
[[self feedbackLabel] setText:#"Working ..."];
[[self doWorkButton] setEnabled:NO];
[self performSelectorInBackground:#selector(performLongRunningWork:) withObject:nil];
}
- (void)performLongRunningWork:(id)obj
{
// simulate 5 seconds of work
// I added a slider to the form - I can slide it back and forth during the 5 sec.
sleep(5);
[self performSelectorOnMainThread:#selector(workDone:) withObject:nil waitUntilDone:YES];
}
- (void)workDone:(id)obj
{
[[self feedbackLabel] setText:#"Done ..."];
[[self doWorkButton] setEnabled:YES];
}
GCD Sample:
// on click of button
- (IBAction)doWork:(id)sender
{
[[self feedbackLabel] setText:#"Working ..."];
[[self doWorkButton] setEnabled:NO];
// async queue for bg work
// main queue for updating ui on main thread
dispatch_queue_t queue = dispatch_queue_create("com.sample", 0);
dispatch_queue_t main = dispatch_get_main_queue();
// do the long running work in bg async queue
// within that, call to update UI on main thread.
dispatch_async(queue,
^{
[self performLongRunningWork];
dispatch_async(main, ^{ [self workDone]; });
});
}
- (void)performLongRunningWork
{
// simulate 5 seconds of work
// I added a slider to the form - I can slide it back and forth during the 5 sec.
sleep(5);
}
- (void)workDone
{
[[self feedbackLabel] setText:#"Done ..."];
[[self doWorkButton] setEnabled:YES];
}
dispatch_queue_t backgroundQueue;
backgroundQueue = dispatch_queue_create("com.images.bgqueue", NULL);
- (void)process {
dispatch_async(backgroundQueue, ^(void){
//background task
[self processHtml];
dispatch_async(main, ^{
// UI updates in main queue
[self workDone];
});
});
});
}
By and large, any work to be submitted to a background queue needs to follow this pattern of code:
dispatch_queue_t queue = dispatch_queue_create("com.myappname", 0);
__weak MyClass *weakSelf = self; //must be weak to avoid retain cycle
//Assign async work
dispatch_async(queue,
^{
[weakSelf doWork];
dispatch_async(dispatch_get_main_queue(),
^{
[weakSelf workDone];
});
});
queue = nil; //Using ARC, we nil out. Block always retains the queue.
Never Forget:
1 - queue variable above is a reference counted object, because it is a private queue, not a global one. So it is retained by the block which is executing inside that queue. Until this task is complete, it is not released.
2 - Every queue got its own stack which will be allocated / deallocated as part of recursive operation. You only need to worry about class member variables which are reference counted (strong, retain etc.) which are accessed as part of doWork above.
3 - While accessing those reference counted vars inside background queue operation, you need to make them thread-safe, depending on use cases in your app. Examples include writes to objects such as strings, arrays etc. Those writes should be encapsulated inside #synchronized keyword to ensure thread-safe access.
#synchronized ensures no another thread can get access to the resource it protects, during the time the block it encapsulates gets executed.
#synchronized(myMutableArray)
{
//operation
}
In the above code block, no alterations are allowed to myMutableArray inside the #synchronized block by any other thread.