I am looking for an Objective-C function to call a function to update the UI at a specified interval (between 2 and 5 seconds). Something that can be used (roughly) like this pseudocode:
array[String]; // already populated
function1(){
if (array.hasMoreElements()){
call function2() in x seconds;
}
}
void function2(){
update gui with next string in the array;
function1();
}
I simply can't use sleep() for x seconds, because the GUI will become unresponsive; and I can't create a new thread to update the GUI because the iOS UI elements are not thread safe.
I have researched ualarm(), but it is old and very crude, and someone told me that there is a similar utility in the iOS library; however I have not been able to find it.
You're talking about Obj-C but writing something like C pseudocode. If you're really writing normal Obj-C, the -performSelector:withObject:afterDelay is the general family you want that drops in here. (You can use "blocks" on iOS 4.x and above). For example in your use case:
- (void)displayNextString
{
if ([strings count] > 0) {
[self updateUIwithString:[strings objectAtIndex:0]];
[strings removeObjectAtIndex:0];
[self performSelector:#selector(displayNextString) withObject:nil afterDelay:2.0];
}
}
You may use NSTimer. This API fits perfectly what you want to do.
You can simply define a method like
-(void)updateUI {
//update interface
}
then do something like
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:interval target:self selector:#selector(updateUI) userInfo:nil repeats:YES];
This way the timer will continue call the method you set. Check the reference to see how to stop or invalidate the timer.
Related
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.
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
}
I have an issue while working with NSTimer.
Let's assume I have this architecture :
ThreadedClass.m (contains a NSTimer* timer;)
- (id) init {
if (self = [super init]) {
// do blablabla
[self launchAThread];
}
return self;
}
- (void) launchAThread {
[NSThread detachNewThreadSelector:#selector(selectorToMyThreadFunction)
toTarget:self
withObject:nil];
}
- (void) selectorToMyThreadFunction {
//I do my stuff in here
//Then i relaunch a Timer to call this function
//periodically but it has to be "atomic" so no
//repeating timer since i don't know the time
//this function will take
//I do some [self changeSomething];
[self restartTimer];
//MyThread ends here (and might be recreated by the Timer's bip
}
- (void)restartTimer {
if (![NSThread isMainThread]) {
[self performSelectorOnMainThread:#selector(restartTimer)
withObject:nil
waitUntilDone:NO];
return;
}
[timer invalidate];
[timer release];
timer = [[NSTimer scheduledTimerWithTimeInterval:interval
target:self
selector:#selector(launchWithTimer:)
userInfo:nil
repeats:NO] retain];
}
- (void) launchWithTimer:(NSTimer *)theTimer {
if (theTimer == timer)
{
[timer release];
timer = nil;
[self launchAThread];
}
else
{
//Nothing to be done in here, a user launch a thread manually
}
}
So let's assume the user of the class alloc it and release it right after. My timer will still be alive and the object too (since there is a retain made by the timer).
When the timer will fire, it will do [self launchAThread] and then the timer will invalidate and release itself AND it will release my object which now has a retainCount = 0... Let's assume, one more time, the object is deallocted right after, this will cause crash and there is nothing i can do to stop it that comes right to my mind.
I agree, this is a lot of assumptions but i'm curious to know if someone already had this issue and how he solved it.
Thanks for reading and I hope I was clear ! :)
Yo have to always invalidate your timer before releasing it. If the timer is a part of a view controller I am always invalidating it in viewWillDisappear. For me is so odd that NSTimers retains theirs owners. I think the best way is to create - (void)cleanUp method, that will invalidate the timer and warn the user of the class to ALWAYS use cleanUp before releasing. If somebody knows the better way I will be glad.
As long as you're not using a repeating timer, why not use dispatch_after? You'll save yourself the headache and overhead of the NSTimer object. And if you just stick with GCD you can avoid the call to detachNewThreadSelector: as well.
NSRunloop will retain the timer for you, which means you don't have retain/release it at all
Since I'm making a library, i can't assume that the user will call a cleanUp function anytime.
So to solve my problem, I added a new "layer" : a new class that will do the thread and timer part so that I am the user of this class and I know I have to cleanUp !
I first resorted to using a cleanUp kind of function too.
I also have an object A owning object B that contains the timer - which retains object B. When object A is deallocated it releases object B, but B lives on because the timer retained it. Then in the method fired but the timer, I'm using a delegate relationship to call back to object A → hard crash. This is where object A needs to "cleanUp" B's timer. This could lead to other problems though if other objects also rely on object B. And also class A shouldn't and might not know the implementation secrets of class B. :-P I guess a good solution could be to unset that delegate relationship before releasing is done in A's dealloc because you don't know if B will die even if you released it - someone else might have retained it (LIKE THAT D*** NSTIMER)... :-P
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
I'm writing a Cocoa application, with a GUI designed in Interface Builder. I need to schedule background activity (at regular intervals) without blocking the UI, so I run it in a separate thread, like this:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
[self performSelectorInBackground:#selector(schedule) withObject:nil];
}
- (void) schedule {
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
NSRunLoop* runLoop = [NSRunLoop currentRunLoop];
timer = [[NSTimer scheduledTimerWithTimeInterval:FEED_UPDATE_INTERVAL
target:activityObj
selector:#selector(run:)
userInfo:nil
repeats:YES]
retain];
[runLoop run];
[pool release];
}
I retain the timer, so I can easily invalidate and reschedule.
Problem: I must also fire the run: method in response to GUI events, so it is synchronous (i.e. a "perform activity" button). Like this:
[timer fire];
I could do this with performSelectorInBackground too, and of course it doesn't block the UI. But this synchronous firings run in another runloop! So I have no guarantee that they won't overlap. How can I queue all of my firings on the same runloop?
[timer setFireDate:[NSDate distantPast]];
I obtained the desired effect by adjusting the next fire date to be ASAP, by passing a past date to setFireDate.
You can use a classic solution for concurrency: semaphore. In your case, the easiest way is to use #synchronized directive. Surround the entire body (or at least, the sensitive part) of run: method with #synchronized. For the synchronization object I suggest you to use a specific ivar or static variable instead of the activityObj's class in order to avoid deadlocks.
-(void)run:(id)param {
// do thread-safe things here
#synchronized(syncObj) {
// put your critical section here
}
// do more thread-safe things here
}
Code in critical section won't overlap.
you should schedule NSTimer on mainThread (and fire the timer to perform the selector --- the selector can be execute on background thread, thus do not block UI), rather than schedule NSTimer on a background thread via GCD, because NSTimer will be added to a NSRunLoop, and every NSRunLoop is associated with a NSTread. So when using GCD, use dispatch_after instead of NSTimer to delay the things to happen.