Adding a delay within a loop - objective-c

Is there any way to add a delay of a fraction of a second to a loop (e.g. A for loop). i.e. I would like a short delay after each iteration.
I know that cocos2d allows you to schedule selecters with a delay. But I am not sure how this could be used in this case.
I also know that sleep is costly and not advisable.
Any suggestions?

You should not use NSTimers in cocos2d. It will cause troubles if you want to have possibility to pause your game.
If you want to loop some action with fixed delay between iterations, you can freely use scedule:interval: method with needed delay.
[self schedule:#selector(methodToScedule) interval:yourDelay]
Or if you have to do random delay, you can use sequenses of cocos2d actions. For example
- (void) sceduleMethod
{
// do anything you want
ccTime randomDuration = // make your random duration
id delayAction = [CCDelayTime actionWithDuration: randomDuration];
id callbackAction = [CCCallFunc actionWithTarget:self selector:#selector(scheduleMethod)];
id sequence = [CCSequenece actionOne: delayAction actionTwo: callbackAction];
[self runAction: sequence];
}
in this case you must call your method only once. Then it will call itself with given delay.

You could use C's sleep function:
sleep(seconds);
But you could also look at UITimer, or possibly a block-based performSelector:withObject:afterDelay: method.
See this for more NSObject-based methods: https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSObject_Class/Reference/Reference.html#//apple_ref/occ/instm/NSObject/performSelector:withObject:afterDelay

Related

How to call a method once per second in objective-c

My program has a loop that does calculation and drawing like 60 times a second. I want my movement action be executed only once a second, how do I do that with objective-c methods?
You can use
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)seconds target:(id)target selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)repeats
For one second:
NSTimer *yourTastTimer = scheduledTimerWithTimeInterval:1 target:yourTarget selector:#selector(yourMethod) userInfo:nil repeats:YES;
You can check the documentation for NSTimer for details.
Edit
You may also want to check this answer.
If you are using Cocos2D for iphone you MUST NOT use the NSTimer.
Following the documentation, you should use the schedule:interval: method like that:
[self schedule: #selector(tick:) interval:1.0f];
- (void) tick: (ccTime) dt
{
//...
}
If your using a CCSprite ,they have a schedule method which might do the trick.
Basically do what for the main loop.
You could also keep a ticker in your main loop and check that this is the ticker%60==0.
Then call your movement action.
I read NSTimer is discouraged with cocos2d here.

Stop performSelector:withObject:afterDelay for loop

Here's a code snippet I'm trying to get to work, but its loop won't stop the way that I want it to:
- (IBAction)methodName:(UIButton*)sender
{
[self loopMethod];
}
-(void) loopMethod
{
for( int index = 0; index < loopLimit; index++ )
{
//code I want to execute
[self performSelector:#selector(loopMethod)
withObject:nil
afterDelay:2.0];
}
}
The code just keeps looping even though I've made the for loop finite. What I want is for the code to execute, pause for two seconds, and then run the loop while the int value is less than the loopLimit I've set.
It's been hinted that this performSelector:withObject:afterDelay: method may not be the right thing to use here but I'm not sure why or what is better to use here.
Any illuminating suggestions?
What's happening here is that the loop is running as quickly as possible, and the performSelector:... calls are happening at that speed. Then, at 2.0001, 2.0004, 2.0010, ... seconds later, method gets called.
The other thing (now that you've edited to make clear that the performSelector:... is ending up calling the same method that it's in, is that the value of your loop's index variable isn't saved between calls. Every time loopMethod is run, the code in the loop starts from the beginning: index is set to zero and counts up. That means that every time the method is run, you end up with loopLimit new calls pending, 2 seconds from then. Each one of those calls in turn spawns a new set, and so on, ad infinitum.
Every run of the loop is in fact finite, but the loop keeps getting run. You need some way to signal that the loop needs to stop, and you can't do that entirely within the loop method. You could put the counter (your index variable) into an ivar; that would make its value persistent across calls to loopMethod, but I think you want to look into using an NSTimer that repeats:
[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:#selector(method:) userInfo:nil repeats:YES];
If you stick this into an ivar, you can keep track of how many times it fires and stop it later. There's a number of posts already on SO about updating text fields in a loop, using a timer like this: https://stackoverflow.com/search?q=%5Bobjc%5D+update+text+field+timer
The reason it doesn't run every 2 seconds is because you are running through the loop, and shooting off that selector after a 2 second delay. In other words, there is no delay in between the loop. If i had to guess, it probably waits 2 seconds, then fires loopLimit times, correct?
For your function to work the way you want it to, it would need to be recursive.
-(void) methodName
{
if(index < loopLimit) {
//code you want to execute
[self performSelector:#selector(**methodName**) withObject:nil afterDelay:2.0];
}
index++;
}
This is a pretty awkward way to do this. An NSTimer is typically what you would use here instead, then you can stop the timer when you are done.
In a function you start the timer like this:
[self setIndex:0];
[self setTimer:[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:#selector(method:) userInfo:nil repeats:YES]];
then this method gets called every time:
-(void)method:(NSTimer *)timer {
if(index >= loopLimit) {
[[self timer] invalidate];
}
//Code you want to execute
}

How to make a periodic call to a method in objective c?

if the question is not explained clearly please excuse me.
I'm developing an iphone Client-Server app, i created all the classes, instances and ect. I can even send get and parse the response too.. Anyway, now i need to make my method be called in a defined period of time(for instance, call it repeatly in 10 seconds). I googled a lot and also take a look at
NSDate but i couldn't solve.. Now, can anyone please help me how to handle this situation? Thank you very much
You can create and schedule an instance of NSTimer to call your method on a given time interval. See in particular the following convenience creation method:
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)seconds target:(id)target selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)repeats
http://developer.apple.com/library/ios/#documentation/cocoa/reference/foundation/Classes/NSTimer_Class/Reference/NSTimer.html
Your best bet is to look into Grand Central Dispatch since you are going to want to run this in the background:
Use NSTimer mentioned above.
What you want is the NSObject method -performSelector:withObject:afterDelay:.
Click for docs.
If you have the method that it calls call this on itself again, you'll have a looping, self-firing delayed poll method.
I'd recommend checking a class variable to see if you really mean it each time, so you can turn it off from outside itself.
Grand Central Dispatch will create a different thread to run on. so if the timer method (shown below and suggested above) lags your app you will need to put the command on a separate thread.
NSTimer is what you should use though. for example if you want to repeat a method that is initiated from a button press you could do this
- (void)viewDidLoad
{
[super viewDidLoad];
[cameraControlButtonUp addTarget:self action:#selector(cameraControlButtonUpPressed)
forControlEvents:UIControlEventTouchUpInside];
}
-(IBAction)buttonDown:(id)sender{
NSInteger tag = [sender tag];
if (tag==1) {
buttonCounter=1;
timer = [[NSTimer scheduledTimerWithTimeInterval:.5 target:self selector:#selector(sendJoin) userInfo:nil repeats:YES]retain];
}
}
-(void)sendJoin
{
switch (buttonCounter) {
case 1:
[cClient userDigitalPushAndRelease:372];
break;
default:
break;
}
}
-(void)cameraControlButtonUpPressed
{
[timer invalidate];
}
that will repeat the command till the button is released. take in mind you need to link the ibaction with the button down event (only the button down event). as well as create timer in the .h and tag the button to 1 that you want to use this with.
for a more basic example; its quite simple. just create your method to call, timer and set repeat to YES. then call invalidate to stop it. i had to create a seperate method sendJoin because i couldnt get the numbers to pass correctly to the method. but if you dont have any parameters its even easier. just use the timer syntax to create it then invalidate it when ur done

iOS performSelectorOnMainThread with multiple arguments

I would like to perform a selector on the main thread from another thread, but the selector has multiple arguments, similar to this:
-(void) doSomethingWith:(int) a b:(float)b c:(float)c d:(float)d e:(float)e {
//...
}
How can I get this working with performSelectorOnMainThread: withObject: waitUntilDone:?
EDIT
I would like to explain why i need this.
I'm working with UIImageViews on the main thread, and I make the calculations for them on another thread. I use a lot of calculations so if i make everything on the main thread, the app lags. I know that UI elements can only be manipulated on the main thread, this is why i would like it to work this way, so the main thread can listen to touch events without lags.
When you're using iOS >= 4, you'd do this instead:
dispatch_async(dispatch_get_main_queue(), ^{
[self doSomething:1 b:2 c:3 d:4 e:5];
});
That's like doing waitUntilDone:NO. If you want to wait until the method is finished, use dispatch_sync instead.
You'll need to use a NSInvocation
Create the object, set the target, selector and arguments.
Then, use
[ invocationObject performSelectorOnMainThread: #selector( invoke ) withObject: nil, waitUntilDone: NO ];
you can pass one object of NSDictionary/NSArray type having required arguments.
and accept the same type of object in your function.
then, decompose the values and proceed with processing.
you have to use NSNumber for numeric values for adding them to NSarray/NSDictionary and later on in your function, you can convert them back with intValue/floatValue etc
best of buck.

How to make sure that certain code is not run more than 1 every 1 seconds?

I have a program that download tons of files from the net
I have several threads downloading data.
After each data is called, I need to update the managed object contexts
If 10 threads finish loading at about the same time, then the managed object context will get run 10 times.
The truth is I only need to run it once.
What I want to do is to create a method that accept a block.
What should I do to make a function that receive a block, but if that block has been run less than 1 second ago, it won't run the block but instead would postpone the second running till 1 seconds no matter how often the function is called.
Mike Ash has already implemented a timer class for this.
You need to init it with a behaviour type, depending on the exact behaviour you want:
MABGTimerDelay means every time you call afterDelay:1.0 do:^{ /*code*/ } it will set the fire date back so that it only gets run a full second after the last call.
MABGTimerCoalesce means every time you call afterDelay:1.0 do:^{ /*code*/ } it will set the fire date back so that it only gets run a full second after the first call.
If it's already been run, both behaviours will allow you to run it again, but only after the delay has passed again.
If the block that gets run is always the same, you could have a loop running on a one second interval that checks a boolean value and only executes the block if the boolean value is YES. Something like this:
BOOL needsUpdate;
-(void) loop {
if (needsUpdate) {
//Run Block
needsUpdate = NO;
}
[self performSelector:#selector(loop) withObject:nil afterDelay:1.0];
}
When the threads finish loading, you just set needsUpdate = YES and the loop takes care of the rest.
When a thread finished call the method in the main thread. In that method make a timer with 1 second delay.
- (void)threadDidFinish
{
if (_saveTimer != nil)
{
_saveTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(saveTimerDidFire) userInfo:nil repeats:NO];
}
}
- (void)saveTimerDidFire
{
[_saveTimer invalidate];
_saveTimer = nil;
// save the changes
}
This code will ensure you will save every second regardless of the number of times threadDidFinish was called. NSTimer* _saveTimer is an instance variable.