GCD: How to change timer fire interval - iphone

This may sound a newbie question anyway, I'm very new to GCD
I've following code :
int interval = 2;
int leeway = 0;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
if (timer) {
dispatch_source_set_timer(timer, dispatch_walltime(DISPATCH_TIME_NOW, NSEC_PER_SEC * interval), interval * NSEC_PER_SEC, leeway);
dispatch_source_set_event_handler(timer, ^{
[self someMethod];
});
dispatch_resume(timer);
}
Where someMethod is :
- (void)someMethod
{
NSLog(#"Thread 1");
}
How do I change the timer's fire interval property in someMethod ?

Got the answer on my own, calling dispatch_source_set_timer with new interval value is enough

Related

Why does not progressBar change in dispatch_group_async?

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);

While button is being pressed

How do i set up a button (IBAction and UIButton attached) to continue to run the IBAction or a function while the button is being pressed, running a functioning continuously until the button is let up.
Should i attach a value changed receiver?
Simple question, but I can't find the answer.
[myButton addTarget:self action:#selector(buttonIsDown) forControlEvents:UIControlEventTouchDown];
[myButton addTarget:self action:#selector(buttonWasReleased) forControlEvents:UIControlEventTouchUpInside];
- (void)buttonIsDown
{
//myTimer should be declared in your header file so it can be used in both of these actions.
NSTimer *myTimer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(myRepeatingAction) userInfo:nil repeats:YES];
}
- (void)buttonWasReleased
{
[myTimer invalidate];
myTimer = nil;
}
Add a dispatch source iVar to your controller...
dispatch_source_t _timer;
Then, in your touchDown action, create the timer that fires every so many seconds. You will do your repeating work in there.
If all your work happens in UI, then set queue to be
dispatch_queue_t queue = dispatch_get_main_queue();
and then the timer will run on the main thread.
- (IBAction)touchDown:(id)sender {
if (!_timer) {
dispatch_queue_t queue = dispatch_get_global_queue(
DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
// This is the number of seconds between each firing of the timer
float timeoutInSeconds = 0.25;
dispatch_source_set_timer(
_timer,
dispatch_time(DISPATCH_TIME_NOW, timeoutInSeconds * NSEC_PER_SEC),
timeoutInSeconds * NSEC_PER_SEC,
0.10 * NSEC_PER_SEC);
dispatch_source_set_event_handler(_timer, ^{
// ***** LOOK HERE *****
// This block will execute every time the timer fires.
// Do any non-UI related work here so as not to block the main thread
dispatch_async(dispatch_get_main_queue(), ^{
// Do UI work on main thread
NSLog(#"Look, Mom, I'm doing some work");
});
});
}
dispatch_resume(_timer);
}
Now, make sure to register for both touch-up-inside and touch-up-outside
- (IBAction)touchUp:(id)sender {
if (_timer) {
dispatch_suspend(_timer);
}
}
Make sure you destroy the timer
- (void)dealloc {
if (_timer) {
dispatch_source_cancel(_timer);
dispatch_release(_timer);
_timer = NULL;
}
}
UIButton should call a starting method with touchDown event and call ending method with touchUpInside event

Main Thread Conflict / Timing Issue - GCD - iPhone

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];

Memory leaks when generating dispatch source timer events

We are using dispatch queues to generate timer events. Following is the code which does the task:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
if (!timer) return self;
dispatch_source_set_timer(timer, dispatch_walltime(NULL, 0), interval * NSEC_PER_SEC, 5 * NSEC_PER_SEC);
dispatch_source_set_event_handler(timer,
^{
//Some work…
});
This works very well except that when we run the profiler, we see a lot of memory leaks from these methods:
dispatch_source_create
dispatch_source_set_timer
dispatch_source_set_event_handler
We had made sure that timer is released using dispatch_release() method.
Can someone please let us know if there is any mistake we are doing in the code above? And also if you can point out any example of timer event generation, it would be helpful.
dispatch_source_set_timer(3) Mac OS X Manual Page
All timers will repeat indefinitely until
dispatch_source_cancel() is called.
How do you call dispatch_source_cancel() and dispatch_release() for the timer?
Dispatch source timer example:
dispatch_source_t timer = dispatch_source_create(
DISPATCH_SOURCE_TYPE_TIMER, 0, 0,
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
dispatch_source_set_timer(timer,
dispatch_time(DISPATCH_TIME_NOW, 1ull * NSEC_PER_SEC),
DISPATCH_TIME_FOREVER, 1ull * NSEC_PER_SEC);
dispatch_source_set_event_handler(timer, ^{
NSLog(#"wakeup!");
dispatch_source_cancel(timer);
});
dispatch_source_set_cancel_handler(timer, ^{
NSLog(#"canceled");
dispatch_release(timer);
});
dispatch_resume(timer);

Asynchronously dispatched recursive blocks

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).