I'd like to show progressbar when someting, but progress bar does not changed.
What I supposed to do?
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
dispatch_group_async(group, queue, ^{
for (int i = 0; i < 100; i++)
{
dispatch_group_async(group, dispatch_get_main_queue(),^{
self.progressBar.progress = (float)i/100;
});
// do big somethings
}
});
In your original question, you were setting progress as an integer between 0 and 100. If this is a UIProgressView, then progress is a floating point and should go from 0 to 1.0.
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
dispatch_group_async(group, queue, ^{
for (int i = 0; i < 100; i++)
{
dispatch_group_async(group, dispatch_get_main_queue(),^{
self.progressBar.progress = ((CGFloat) i) / 100.0;
});
// do big somethings
}
});
Or, more simply, since you're not really using the group (you only have one operation on the global queue, and groups are generally redundant when dealing with serial queues):
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
dispatch_async(queue, ^{
for (int i = 0; i < 100; i++)
{
dispatch_async(dispatch_get_main_queue(),^{
self.progressBar.progress = ((CGFloat) i) / 100.0;
});
// do big somethings
}
});
If you're still not seeing the progress view update, then it's got to be something simple like an IBOutlet that isn't hooked up correctly. Make sure self.progressBar is not nil. Try setting its progress to 0.5 and make sure that gets reflected properly.
I suggest using
dispatch_queue_t queue = dispatch_queue_create("com.company.myqueue", NULL);
to update progress status, because
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
is concurrent queue.
You should update your progress bar in the main thread :
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
// async stuff
dispatch_sync(dispatch_get_main_queue(), ^{
// sync stuff
});
});
dispatch_release(queue);
Related
I just want to prerender different images for fast access.
I use grand central dispatch here to execute the different blocks.
After starting the queue, I want to set the first image when its done.
With the current code below, unfortunately the first image will only be displayed when all images has been rendered.
So how can i modify the code? Is it possible to get a delegate when each image has finished?
Here´s the code:
// Async prerendering
for (int i = 0; i < count; i++) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
dispatch_async(dispatch_get_main_queue(), ^{
UIImage* finalImage = [self prerenderImageForIndex:i];
[self.imageArray addObject:finalImage];
// TODO: i want to display the first image.
// rendering goes on in the background
if (i==0 && [self.imageArray objectAtIndex:0] != nil ) {
self.image = [self.imageArray objectAtIndex:0];
}
});
});
}
Update:
-(UIImage*) prerenderImageForIndex:(int)frame {
UIGraphicsBeginImageContextWithOptions(CGSizeMake(self.frame.size.width, self.frame.size.height), NO, 0);
for (int i=0; i< [configurationArray count]; i++) {
//... get the layerName
UIImage* layerImage = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:layerName ofType:#"png"]];
// draw layer with blendmode and alpha
[layerImage drawInRect:CGRectMake(x, y, layerImage.size.width, layerImage.size.height)
blendMode:layerblendmode
alpha: layeralpha];
}
// Get current context as an UIImage
UIImage* finalImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return finalImage;
}
i just want to know how do i cancel/stop or restart a running queue?
Is that possible?
Thanks for your help.
you have to use a serial queue, which executes FIFO for example:
dispatch_queue_t queue;
queue = dispatch_queue_create("myImageQueue", NULL);
for(int i = 0; i<count; i++) {
dispatch_async(queue, ^{
// do your stuff in the right order
});
}
for serial Dispatch queues look at:
http://developer.apple.com/library/ios/#documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html
I'm not sure why you have the dispatch_async calls nested like this but maybe that's the problem. I would imagine something like below would accomplish what you want. You only need to get the main queue when you actually want to do the UI update, everything else should be done on the background queue.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
UIImage* finalImage = [self prerenderImageForIndex:i];
[self.imageArray addObject:finalImage];
if (i==0 && [self.imageArray objectAtIndex:0] != nil ) {
dispatch_async(dispatch_get_main_queue(), ^{
self.image = [self.imageArray objectAtIndex:0];
});
}
});
How to make a backgroundthread not to work until another background thread gets completed and how to make it to start it's thread once the first backgroundthread gets completed
Use flag to handle such type of event as shown below...
BOOL isYourthreadexecuting = NO;
- (void)beginThread {
isYourthreadexecuting = YES;
[self performSelectorInBackground:#selector(backgroundThread) withObject:nil];
}
- (void)backgroundThread {
[myClass performLongTask];
// Done!
isYourthreadexecuting = NO;
}
- (void)waitForThread {
if (! isYourthreadexecuting) {
// Thread completed
[self callyourmethod];
}
}
Edited >> Addition according use comment
I suggest you to use NSOperationQueue for multithreading.
Hope, this will you...
As stated in my comment, you can use GCD's Serial Dispatch Queues. Here is a sample code to demonstrate:
- (IBAction)buttonSerialQ2Pressed:(id)sender
{
dispatch_queue_t serialdQueue;
serialdQueue = dispatch_queue_create("com.mydomain.testbed.serialQ2", NULL);
dispatch_async(serialdQueue, ^{
//your code here
[self method1];
});
dispatch_async(serialdQueue, ^{
//your code here
[self method2];
});
dispatch_async(serialdQueue, ^{
//your code here
[self method2];
});
dispatch_async(serialdQueue, ^{
//your code here
[self method3];
});
}
-(void)method1
{
for (int i=0; i<1000; i++)
{
NSLog(#"method1 i: %i", i);
}
}
-(void)method2
{
for (int i=0; i<10; i++)
{
NSLog(#"method2 i: %i", i);
}
}
-(void)method3
{
for (int i=0; i<100; i++)
{
NSLog(#"method3 i: %i", i);
}
}
Scenario:
User taps a button asking for some kind of modification on address book.
A method is called to start this modification and an alert view is shown.
In order to show the alert view and keep the UI responsive, I used dispatch_queue:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_sync(dispatch_get_main_queue(), ^{
// Show the alert view
});
});
Start the process of address book modification using:
dispatch_async(modifyingAddressBookQueue, ^{});
Now, I want to provide the user with the ability to cancel the process anytime (of course before saving the address book). So when he taps the cancel button in the alert sheet, I want to access the dispatch block, set some certain BOOL to stop the process and revert the address book.
The problem is, you can't do that! you can't access the block and change any variable inside it since all variables are copied only once. Any change of variables inside the block while being executed won't be seen by the block.
To sum up: How to stop a going operation using a UI event?
Update:
The code for the process:
- (void) startFixingModification {
_fixContacts = YES;
__block BOOL cancelled = NO;
dispatch_queue_t modifyingAddressBookQueue;
modifyingAddressBookQueue = dispatch_queue_create(sModifyingAddressBookQueueIdentifier,
NULL);
dispatch_async(modifyingAddressBookQueue, ^{
for (NSMutableDictionary *contactDictionary in _contactArray) {
if (!cancelled) {
break;
}
i = i + 1;
BOOL didFixContact = [self fixNumberInContactDictionary:contactDictionary];
if (!didFixContact) {
_fixedNumbers = _fixedNumbers - 1;
}
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_sync(dispatch_get_main_queue(), ^{
[self setAlertViewProgress:i];
});
});
}
});
cancelledPtr = &cancelled;
}
Code for alertview (my own lib) delegate
- (void) alertViewProgressCancel:(ASAlertViewProgress *)alertView { // This is a private lib.
if (cancelledPtr)
{
NSLog(#"stopping");
*cancelledPtr = YES;
}
}
In interface, I declare
BOOL* cancelledPtr;
Update 2:
It's getting really frustrating! for the following code
for (NSMutableDictionary *contactDictionary in _contactArray) {
NSLog(#"%d", _cancelModification);
if (_cancelModification) {
break;
}
}
if _cancelModification is set to YES, the for loop is broken and that's OK. Once I comment out the NSLog line, the _cancelModification is neglected when it changes to YES!
If you declare your BOOL using __block, then it can be changed outside of the block execution, and the block will see the new value. See the documentation for more details.
An example:
#interface SNViewController ()
{
BOOL* cancelledPtr;
}
#end
#implementation SNViewController
- (IBAction)start:(id)sender
{
__block BOOL cancelled = NO;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
while (!cancelled) {
NSLog(#"running");
sleep(1);
}
NSLog(#"stopped");
});
cancelledPtr = &cancelled;
}
- (IBAction)stop:(id)sender
{
if (cancelledPtr)
{
NSLog(#"stopping");
*cancelledPtr = YES;
}
}
#end
Alternatively, use an ivar in your class to store the BOOL. The block will implicitly make a copy of self and will access the ivar via that. No need for __block.
#interface SNViewController ()
{
BOOL cancelled;
}
#end
#implementation SNViewController
- (IBAction)start:(id)sender
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
while (!cancelled) {
NSLog(#"running");
sleep(1);
}
NSLog(#"stopped");
});
}
- (IBAction)stop:(id)sender
{
NSLog(#"stopping");
cancelled = YES;
}
#end
Approach 1
Create a custom dispatch_async method that returns a "cancelable" block.
// The dispatch_cancel_block_t takes as parameter the "cancel" directive to suspend the block execution or not whenever the block to execute is dispatched.
// The return value is a boolean indicating if the block has already been executed or not.
typedef BOOL (^dispatch_cancel_block_t)(BOOL cancelBlock);
dispatch_cancel_block_t dispatch_async_with_cancel_block(dispatch_queue_t queue, void (^block)())
{
__block BOOL execute = YES;
__block BOOL executed = NO;
dispatch_cancel_block_t cancelBlock = ^BOOL (BOOL cancelled) {
execute = !cancelled;
return executed == NO;
};
dispatch_async(queue, ^{
if (execute)
block();
executed = YES;
});
return cancelBlock;
}
- (void)testCancelableBlock
{
dispatch_cancel_block_t cancelBlock = dispatch_async_with_cancel_block(dispatch_get_main_queue(), ^{
NSLog(#"Block 1 executed");
});
// Canceling the block execution
BOOL success1 = cancelBlock(YES);
NSLog(#"Block is cancelled successfully: %#", success1?#"YES":#"NO");
// Resuming the block execution
// BOOL success2 = cancelBlock(NO);
// NSLog(#"Block is resumed successfully: %#", success2?#"YES":#"NO");
}
Approach 2
Defining a macro for executing a block asynchronously if a condition is validated:
#define dispatch_async_if(queue,condition,block) \
dispatch_async(queue, ^{\
if (condition == YES)\
block();\
});
- (void)testConditionBlock
{
// Creating condition variable
__block BOOL condition = YES;
dispatch_async_if(dispatch_get_main_queue(), condition, ^{
NSLog(#"Block 2 executed");
});
// Canceling the block execution
condition = NO;
// Also, we could use a method to test the condition status
dispatch_async_if(dispatch_get_main_queue(), ![self mustCancelBlockExecution], ^{
NSLog(#"Block 3 executed");
});
}
Try to apply the following code sample to your situation:
__block UIView * tempView = [[UIView alloc] initWithFrame:CGRectMake(50, 100, 220, 30)];
[tempView setBackgroundColor:[UIColor grayColor]];
[self.view addSubview:tempView];
[tempView release];
__block BOOL cancel = NO;
//点击之后就会开始执行这个方法
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
int i = 0;
while (i < 1000000000 && cancel == NO) {
i++;
}
NSLog(#"Task end: i = %d", i);
//这个不会执行,因为在之前,gcd task已经结束
[tempView removeFromSuperview];
});
//1s 之后执行这个方法
double delayInSeconds = 1.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
NSLog(#"A GCD Task Start");
cancel = YES;
[tempView setBackgroundColor:[UIColor blackColor]];
});
I have the following dispatch queue my app :
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^ {
[activeModel freeUpMallocedData];
// UI Updates have to be made on the main thread, so request from GCD.
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^ {
[mainViewController removeTidyUpScreen];
[mainViewController showSceneList];
[activeModel release];
});
});
The freeUpMallocedData method updates a UI Progress View :
- (void) freeUpMallocedData {
// Calculate the percentage increase for each item in the pointerStorageArray as it is released so we can update the Progress Screen.
float arrayCount = [pointerStorageArray count];
float incrementCounter = 1 / arrayCount; // Caculates 1% of pointerStorageArray
float newValue = incrementCounter;
int stepCounter = 0;
NSString * message;
// Now iterate through the pointerStorageArray and free all malloced memory.
for (NSValue * value in pointerStorageArray) {
stepCounter ++;
newValue = newValue + incrementCounter;
message = [NSString stringWithFormat:#"Freeing Up Memory (%d of %d) ...", stepCounter, (int)arrayCount];
free(value);
[self tidyUpProgress:message amount:newValue];
}
}
The tidyUpProgress method then executes on the main thread.
- (void) tidyUpProgress: (NSString *) progressMsg amount: (float) amount {
if (tidyUpMonitorDelegate) {
tidyUpProgressMsg = progressMsg;
tidyUpProgressAmount = amount;
[tidyUpMonitorDelegate performSelectorOnMainThread:#selector(model3DTidyUpProgressUpdate) withObject:nil waitUntilDone:NO];
}
}
- (void) model3DTidyUpProgressUpdate {
progressView.progress = app.activeModel.tidyUpProgressAmount;
loadingStatus.text = app.activeModel.tidyUpProgressMsg;
}
The problem is that the app crashes when the freeUpMallocedData method completes. The reason for this is that my initial dispatch queue moves on to request the main queue which then releases activeView. This seems to hijack the thread from the tidyUpMonitorDelegate before it can perform its last update - when it gets the main thread back the activeView has been released and therefore the app crashes as the model3DTidyUpProgresUpdate method is requesting access to variable in a class which has now been dealloced.
Can anyone advise on how to fix this timing issue ?
Thank you in advance.
Just a thought - try renaming the variable inside the dispatch:
dispatch_queue_t mainqueue = dispatch_get_main_queue();
dispatch_async(mainqueue, ^ {
You use two different mechanisms to schedule tasks in the main thread : dispatch_asyc and performSelectorInMainThread:withObject:waitUntilDone:. Each mechanism uses its own queue, which is read by the main run loop.
The order in which those queue are read is undefined. Thus, a task scheduled by performSelectorInMainThread:withObject:waitUntilDone may be performed after (or before) a task scheduled with dispatch_async regardless of which was scheduled first.
You should update tidyUpProgress: to use dispatch_async. Then the order will be guaranteed.
Moreover, after releasing an object, you should always nullify the variable that holds the reference to that object.
this is wrong way:
float arrayCount = [pointerStorageArray count];
-correct way:
NSUinteger arrayCountInt = [pointerStorageArray count];
NSNumber *arrayCountNumber = [NSNumber numberWithUnsignedInteger:arrayCountInt]
float arrayCount = [arrayCountNumber floatValue];
Suppose I run this code:
__block int step = 0;
__block dispatch_block_t myBlock;
myBlock = ^{
if(step == STEPS_COUNT)
{
return;
}
step++;
dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC / 2);
dispatch_after(delay, dispatch_get_current_queue(), myBlock);
};
dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC / 2);
dispatch_after(delay, dispatch_get_current_queue(), myBlock);
The block is invoked once from outside. When the inner invocation is reached, the program crashes without any details. If I use direct invocations everywhere instead of GCD dispatches, everything works fine.
I've also tried calling dispatch_after with a copy of the block. I don't know if this was a step in the right direction or not, but it wasn't enough to make it work.
Ideas?
When trying to solve this problem, I found a snippet of code that solves much of the recursive block related issues. I have not been able to find the source again, but still have the code:
// in some imported file
dispatch_block_t RecursiveBlock(void (^block)(dispatch_block_t recurse)) {
return ^{ block(RecursiveBlock(block)); };
}
// in your method
dispatch_block_t completeTaskWhenSempahoreOpen = RecursiveBlock(^(dispatch_block_t recurse) {
if ([self isSemaphoreOpen]) {
[self completeTask];
} else {
double delayInSeconds = 0.3;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), recurse);
}
});
completeTaskWhenSempahoreOpen();
RecursiveBlock allows for non-argument blocks. It can be rewritten for single or multiple argument blocks. The memory management is simplified using this construct, there is no chance of a retain cycle for example.
My solution was derived entirely from Berik's, so he gets all the credit here. I just felt that a more general framework was needed for the "recursive blocks" problem space (that I haven't found elsewhere), including for the asynchronous case, which is covered here.
Using these three first definitions makes the fourth and fifth methods - which are simply examples - possible, which is an incredibly easy, foolproof, and (I believe) memory-safe way to recurse any block to arbitrary limits.
dispatch_block_t RecursiveBlock(void (^block)(dispatch_block_t recurse)) {
return ^() {
block(RecursiveBlock(block));
};
}
void recurse(void(^recursable)(BOOL *stop))
{
// in your method
__block BOOL stop = NO;
RecursiveBlock(^(dispatch_block_t recurse) {
if ( !stop ) {
//Work
recursable(&stop);
//Repeat
recurse();
}
})();
}
void recurseAfter(void(^recursable)(BOOL *stop, double *delay))
{
// in your method
__block BOOL stop = NO;
__block double delay = 0;
RecursiveBlock(^(dispatch_block_t recurse) {
if ( !stop ) {
//Work
recursable(&stop, &delay);
//Repeat
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), dispatch_get_main_queue(), recurse);
}
})();
}
You'll note that in the following two examples that the machinery of interacting with the recursion mechanism is extremely lightweight, basically amounting to having to wrap a block in recurse and that block must take a BOOL *stop variable, which should be set at some point to exit recursion (a familiar pattern in some of the Cocoa block iterators).
- (void)recurseTo:(int)max
{
__block int i = 0;
void (^recursable)(BOOL *) = ^(BOOL *stop) {
//Do
NSLog(#"testing: %d", i);
//Criteria
i++;
if ( i >= max ) {
*stop = YES;
}
};
recurse(recursable);
}
+ (void)makeSizeGoldenRatio:(UIView *)view
{
__block CGFloat fibonacci_1_h = 1.f;
__block CGFloat fibonacci_2_w = 1.f;
recurse(^(BOOL *stop) {
//Criteria
if ( fibonacci_2_w > view.superview.bounds.size.width || fibonacci_1_h > view.superview.bounds.size.height ) {
//Calculate
CGFloat goldenRatio = fibonacci_2_w/fibonacci_1_h;
//Frame
CGRect newFrame = view.frame;
newFrame.size.width = fibonacci_1_h;
newFrame.size.height = goldenRatio*newFrame.size.width;
view.frame = newFrame;
//Done
*stop = YES;
NSLog(#"Golden Ratio %f -> %# for view", goldenRatio, NSStringFromCGRect(view.frame));
} else {
//Iterate
CGFloat old_fibonnaci_2 = fibonacci_2_w;
fibonacci_2_w = fibonacci_2_w + fibonacci_1_h;
fibonacci_1_h = old_fibonnaci_2;
NSLog(#"Fibonnaci: %f %f", fibonacci_1_h, fibonacci_2_w);
}
});
}
recurseAfter works much the same, though I won't offer a contrived example here. I am using all three of these without issue, replacing my old -performBlock:afterDelay: pattern.
It looks like there are no problem except delay variable. The block uses always the same time that is generated at line 1. You have to call dispatch_time every time if you want to delay dispatching the block.
step++;
dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC / 2);
dispatch_after(delay, dispatch_get_current_queue(), myBlock);
};
EDIT:
I understand.
The block is stored in stack by the block literal. myBlock variable is substituted for the address of the block in stack.
First dispatch_after copied the block from myBlock variable that is the address in stack. And this address is valid at this time. The block is in the current scope.
After that, the block is scoped out. myBlock variable has invalid address at this time. dispatch_after has the copied block in heap. It is safe.
And then, second dispatch_after in the block tries to copy from myBlock variable that is invalid address because the block in stack was already scoped out. It will execute corrupted block in stack.
Thus, you have to Block_copy the block.
myBlock = Block_copy(^{
...
});
And don't forget Block_release the block when you don't need it any more.
Block_release(myBlock);
Opt for a custom dispatch source.
dispatch_queue_t queue = dispatch_queue_create( NULL, DISPATCH_QUEUE_SERIAL );
__block unsigned long steps = 0;
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, queue);
dispatch_source_set_event_handler(source, ^{
if( steps == STEPS_COUNT ) {
dispatch_source_cancel(source);
return;
}
dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC / 2);
dispatch_after(delay, queue, ^{
steps += dispatch_source_get_data(source);
dispatch_source_merge_data(source, 1);
});
});
dispatch_resume( source );
dispatch_source_merge_data(source, 1);
I think you have to copy the block if you want it to stick around (releasing it when you don't want it to call itself anymore).