Register FAQ / Rules Forum Spy Search Today's Posts Mark Forums Read
Go Back   MacRumors Forums > Apple Systems and Services > Programming > Mac Programming

Reply
 
Thread Tools Search this Thread Display Modes
Old Jul 26, 2013, 05:40 PM   #1
Domenachi
macrumors newbie
 
Join Date: Jul 2013
Fundamental I missed - series

I've read most of a couple of good books over the past couple years on iOS programming and went out of practice for a bit but coming back into now and there's a simple fundamental thing I can't wrap my head around when it comes to Objective-C and it's how events happen. Specifically how do I have a series of events happen rather than not all at one time. Here's a basic example I made to show you what I mean - I have a feeling I'm just approaching this all wrong and so my "work-around" is a terrible idea or whatever - I don't know.

Let's say I have a button called "goButton" and a label called "label". When I click the button I want the label to wait 2 seconds and read "Red", and then wait 2 more seconds and read "Yellow" and then 2 more seconds and read "Green". So when I looked up how to use "afterDelay" and did it the way I thought it would be done, it basically just waited 2 seconds and then changed to "Green" - which leads me to believe that all 3 of those delays happened at one time and we just arrived at the end - "Green". So I changed it to this to "work-around" but I feel like there must be a better way to approach this and that I just don't really understand how things happen when going through this code. In my mind if you have a line that has a delay, it should have to wait that delay out before going to the next line, but maybe that would be true if these were separate actions and after the delay you have to call the next action too? But that seems even messier. Here's my work-around that works but just doesn't seem right nor very flexible - obviously the work-around is me adding the extra delays to the successive events (so the delays are 2, 4, and then 6):

Code:
-(IBAction)goButton:(id)sender {
    [self performSelector:@selector(red) withObject:nil afterDelay:2];
    [self performSelector:@selector(yellow) withObject:nil afterDelay:4];
    [self performSelector:@selector(green) withObject:nil afterDelay:6];
}

-(void)red {
    label.text = @"Red";
}

-(void)yellow {
    label.text = @"Yellow";
}

-(void)green {
    label.text = @"Green";
}
Thanks for any help with this. I think I'm missing a fundamental part of Objective C and it probably turns this into a really stupid question but I really appreciate even making me feel dumb at this point. thanks again
Domenachi is offline   0 Reply With Quote
Old Jul 26, 2013, 06:02 PM   #2
chown33
macrumors 603
 
Join Date: Aug 2009
Post the code that didn't work. Specifically, you wrote:
Quote:
So when I looked up how to use "afterDelay" and did it the way I thought it would be done, it basically just waited 2 seconds and then changed to "Green" - which leads me to believe that all 3 of those delays happened at one time and we just arrived at the end - "Green".
So post that code.


The code you posted, with a 2-sec, 4-sec, and 6-sec delay, is one way that occurs to me. It even seems to me like a fairly obvious approach. Clearly, it wasn't an obvious approach for you, but since you didn't describe your algorithm or post any code for it, no one knows what that non-working approach was.

I can think of other ways of doing a series of delays, but rather than describing each of them, it's simpler if you post the one you expected to work but didn't. Someone could then explain why it didn't work.

EDIT
And I see the root of the problem right here:
Quote:
In my mind if you have a line that has a delay, it should have to wait that delay out before going to the next line,
This is wrong.

Reread the docs for the method being called. It should explain that it schedules the target method for execution after a delay. It does not say that the delay is immediately performed, and the method won't return until after the delay. If the docs say it uses a timer, then you should refer to the NSTimer docs for more info.

There are functions that do a "hard delay", i.e. they don't return until after the delay. These won't work in this situation, because during the delay, nothing else can happen: no user interaction, etc. For an example of such a function, look up nanosleep().

Last edited by chown33; Jul 26, 2013 at 06:10 PM.
chown33 is offline   0 Reply With Quote
Old Jul 26, 2013, 10:26 PM   #3
ArtOfWarfare
macrumors 603
 
ArtOfWarfare's Avatar
 
Join Date: Nov 2007
Send a message via Skype™ to ArtOfWarfare
If you want a 2 second delay between red and yellow, then red should be the function where yellow gets scheduled.

The methods return immediately.

Alternatively, you could just do:

Code:
[self red];
sleep(2000);
[self yellow];
sleep(2000);
[self green];
That code should be placed in its own thread, though, to ensure it doesn't block the main one where user interactions take place.
__________________
Battery Status - On the Mac App Store
The only app that'll estimate when your wireless devices will need their batteries changed.
Including the ones paired with other Macs on your network.
ArtOfWarfare is online now   0 Reply With Quote
Old Jul 26, 2013, 10:43 PM   #4
ChristianJapan
macrumors Demi-God
 
ChristianJapan's Avatar
 
Join Date: May 2010
Location: 日本
What I'm missing is the request to redraw the label in each color-methods ...

Code:
[label setNeedsDisplay];
Even better this way:

Code:
[label performSelectorOnMainThread:@selector(setNeedsDisplay) withObject:nil waitUntilDone:NO];
ChristianJapan is offline   0 Reply With Quote
Old Jul 26, 2013, 10:53 PM   #5
ArtOfWarfare
macrumors 603
 
ArtOfWarfare's Avatar
 
Join Date: Nov 2007
Send a message via Skype™ to ArtOfWarfare
Quote:
Originally Posted by ChristianJapan View Post
What I'm missing is the request to redraw the label in each color-methods ...

Code:
[label setNeedsDisplay];
Even better this way:

Code:
[label performSelectorOnMainThread:@selector(setNeedsDisplay) withObject:nil waitUntilDone:NO];
Label's perform setNeedsDisplay themselves as you change their properties - you don't need to set it for them.
__________________
Battery Status - On the Mac App Store
The only app that'll estimate when your wireless devices will need their batteries changed.
Including the ones paired with other Macs on your network.
ArtOfWarfare is online now   0 Reply With Quote
Old Jul 26, 2013, 11:33 PM   #6
ChristianJapan
macrumors Demi-God
 
ChristianJapan's Avatar
 
Join Date: May 2010
Location: 日本
Quote:
Originally Posted by ArtOfWarfare View Post
Label's perform setNeedsDisplay themselves as you change their properties - you don't need to set it for them.
Also outside main thread ?
ChristianJapan is offline   0 Reply With Quote
Old Jul 27, 2013, 02:13 AM   #7
Sonnestah
Banned
 
Join Date: Mar 2013
Quote:
Originally Posted by ArtOfWarfare View Post
If you want a 2 second delay between red and yellow, then red should be the function where yellow gets scheduled.

The methods return immediately.

Alternatively, you could just do:

Code:
[self red];
sleep(2000);
[self yellow];
sleep(2000);
[self green];
That code should be placed in its own thread, though, to ensure it doesn't block the main one where user interactions take place.
Careful, he is updating UI, and that must be done from main thread

He is going to have to mix a background thread with the main thread
Sonnestah is offline   0 Reply With Quote
Old Jul 27, 2013, 08:46 AM   #8
Domenachi
Thread Starter
macrumors newbie
 
Join Date: Jul 2013
If I add another layer to this and I think you'll see where the way I'm doing it starts making trouble.

Let's add a label to the ui - countDownLabel
And then let's say instead of just doing a delay, each of those actions causes countDownLabel to read "3" and then "2" and then "1", then changes the main label's text. So you hit "Go", and then countDownLabel reads 3, 2, and then 1 and then the main label changes to red, and then the countDownLabel count downs again and then changes main label to "yellow", etc.

I could have an action for each count and a delay of 1 second between each one, but now I'll have 9 actions and no flexibility. Because what if instead of a constant starting point for the countdown I want variables for the countdowns.

As in, you hit "Go" and 3 random numbers are selected as the starting points for the countdowns. randRedCount, randYellowCount, randGreedCount. Then it starts counting down from randRedCount, gets to zero and changes the main label to read "Red", then does the same for yellow and then green.

thanks again
Domenachi is offline   0 Reply With Quote
Old Jul 27, 2013, 07:40 PM   #9
chown33
macrumors 603
 
Join Date: Aug 2009
Make a class. An instance of that class is responsible for everything dealing with whatever labels, delays, etc. you need. (Look up "encapsulation".)

That class has one method that is the target method. You then set it up so an initialized instance of the class has the right random numbers, the right counts between yellow, red, and greem or whatever.

All the delay intervals are encapsulated in the target method, too. So each time this target method runs, i.e. each time a delay interval expires, the method runs, does whatever is appropriate for that specific expiration, and then reschedules itself to be called again after whatever delay interval is appropriate.

When the object reaches the end of the actions on labels, countdowns, whatever, it does the past action and then DOESN'T reschedule a new delay.

If the version with all the delays, random numbers, whatever is too complex for you to write, then start by making a class that only handles your original red yellow green problem. Solve the simple problem first. Make sure it works. If you want comments, post its code. Then with a working simple version, start making small changes that add one feature at a time. Test again to make sure it works. Repeat.

Last edited by chown33; Jul 27, 2013 at 07:45 PM.
chown33 is offline   0 Reply With Quote
Old Jul 28, 2013, 08:40 AM   #10
Domenachi
Thread Starter
macrumors newbie
 
Join Date: Jul 2013
ah, I got you. Good plan. thank you.
Domenachi is offline   0 Reply With Quote
Old Jul 28, 2013, 10:54 AM   #11
firewood
macrumors 603
 
Join Date: Jul 2003
Location: Silicon Valley
Cocoa is event driven. Thus your app should never wait. It should always quit running ASAP (completely return from functions and methods). And then wait for the OS to call it back (after telling the OS what to call on some event or timer or completion). The goal to run your app is to have it not run (unless it's computationally intensive or something) most of the time.

Learn to use the return statement a lot. (And blocks as well.)

After the first label change, just return. (After setting up what you what to do later in another callback or block.)
firewood is offline   0 Reply With Quote

Reply
MacRumors Forums > Apple Systems and Services > Programming > Mac Programming

Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes

Similar Threads
thread Thread Starter Forum Replies Last Post
How could we have missed this? chumawumba iPhone 3 Jan 30, 2013 07:04 PM

Forum Jump

All times are GMT -5. The time now is 01:21 PM.

Mac Rumors | Mac | iPhone | iPhone Game Reviews | iPhone Apps

Mobile Version | Fixed | Fluid | Fluid HD
Copyright 2002-2013, MacRumors.com, LLC