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
Related
The following code works good for me:
In the init method of a menu layer:
CCMenuItemFont *item1 = [CCMenuItemFont itemWithString:#"Level 1" target: self selector: #selector(startLevel:)];
item1.userData = (__bridge void*) ([NSNumber numberWithInt:1]);
...//create menu and add in the item1
-(void)startLevel: (CCMenuItem *)sender
{
NSNumber *number = sender.userData;
...
}
My questions are:
I didn't pass item1 when call the method startLevel: how does it know that the sender is item1?
is it written into selector? or is it written in cocoa?
CCMenuItem passes itself as parameter to this selector. Details are in CCMenuItem source code.
Regarding omitting passing itself as a parameter, do you mean like...
- (void) pushedStart : (id) sender
{
//start game
}
but you can't do
[self pushedStart];
because it needs a parameter? If so, what you can do this:
id junkID;
[self pushedStart: junkID];
JunkID will initialize to whatever the hell it is an unassigned ID assigns to, so you pass it as a reference and just don't use it for anything, which is good if you want to have a "start game" button but have the game automatically start inside of a timer or whatever else you're doing with your buttons
As a side note, and getting more into the guts of cocoa, the way it KNOWS (and what YOU must not forget) is that colon. When you call a function you put the variable after a colon [self eat: food];
When you put together the menu item you set it up with target:self, which makes the button use itself (not the layer "self" you use when you call [self eatABanana]) as a target. The button push of
menuButton = target:self selector:#selector(pushButton:)
is represented like
[self pushButton:menuButton]
If you forgot that colon, it's the same as calling a function and not passing the variable, which doesn't give a very helpful error message insofar as it doesn't help you locate where the problem is occurring. I've spent hours upon hours chasing down memory crashes resulting from writing #selector(startGame) instead of #selector(startGame:) in those damn menu buttons. I always feel stupid when I finally figure it out.
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.
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
We had a bug, and it destroys the looks of our UI, some of the UI elements overlap, or has been added to the subview twice. the bug is hardly reproduced so its hard to fix it. Now I thought of the reason, and probably the method that changes the UI are being called twice at the same time. And I was correct, I tried to create the bug programatically.
We have a bug which is caused by a method being accessed by different threads at the same time. To emulate this problem, and better understand it. see codes posted below.
When I do this, updatePresence Method call, my program works perfectly
ViewController.m
-(void)loadConversationScreen{
[conversationController updatePresence];
}
But when I do this, something goes wrong with my program
ViewController.m
-(void)loadConversationScreen{
[conversationController performSelectorInBackground:#selector(updatePresence) withObject:nil];
[conversationController updatePresence];
}
This is because the method is being accessed at the same time and and the instance of my UIView is being accessed/changed also at the same time.
How do I PROPERLY stop 2 threads from using a method at the same time?
How do I properly handle it in IOS(if there is no proper way, what are the work arounds), are there built in locks or somekind?
My app should support ios 4.0 and up
Advance thanks to all for your help.
The best thread lock for you is #sycnhronized(object) {}. This means only one thread can enter the code at a time. The object passed in is used to perform the lock; only one thread can enter a block protected by a particular object's synchronized at a time. The others will wait. This can be any Objective-C object, even a NSString.
Typically, you'd use whatever object you're trying to protect from multiple threads. You probably want #synchronized(self) {}:
-(void)updateVariables {
#synchronized(self) {
_foo = 1;
_bar = 2;
}
}
#sycnhronized is re-entrant in the sense that the same thread can call #sycnhronized as deeply as it wants, for instance:
- (void)a {
#synchronized(self) {
// entered "immediately" if called from b, where the #synchronized has
// already been called
_foo = _foo + 1;
}
}
- (void)b {
#synchronized(self) {
[self a];
}
}
For posterity and because I already typed it before reading your clarification, if you really cared only about updating the UI, you'd want to force your call over to the main thread instead like this:
- (void)someTask {
dispatch_async( dispatch_get_main_queue(), ^{
[self updateUI];
});
}
- (void)updateUI {
NSAssert( [NSThread isMainThread], #"called from non-main thread" );
// do UI updates here
}
As warrenm said you shouldn't update your UIView from a different thread than the Main thread (UI thread). Still, you asked if there is any workaround for what's going on. To be honest, you should try to, instead of blocking the access of the second thread to your method, understand why the methods is called twice. This is more a logical problem than anything else and you should try to fix that, instead of trying a shortcut.
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.