Running a loop Cocoa style

Discussion in 'Mac Programming' started by bahlquist, Dec 6, 2010.

  1. macrumors member

    Joined:
    Oct 6, 2010
    #1
    I wrote a program that used an NSTimer to send a message repeatedly to the same target. Now I want to remove the timer and just have the message sent at the start of each run loop. What is the best way to do this? If I was programming in C, I would just use a while loop.
     
  2. Moderator emeritus

    robbieduncan

    Joined:
    Jul 24, 2002
    Location:
    London
    #2
  3. macrumors G5

    gnasher729

    Joined:
    Nov 25, 2005
    #3
    This doesn't make sense. The run loop is executed exactly as often as it needs to, no more, and no less. There would be no guarantee how often your code would get executed. So the best way to do this is - don't. But why don't you tell us what you actually want to achieve?
     
  4. macrumors 6502a

    GorillaPaws

    Joined:
    Oct 26, 2003
    Location:
    Richmond, VA
    #4
    I suspect you might want to create some form of delegation or notification so your method is called when a certain periodic action occurs. As gnasher729 has said, let us know what you're trying to accomplish.
     
  5. thread starter macrumors member

    Joined:
    Oct 6, 2010
    #5
    I just want the same method called repeatedly. In C:

    Code:
    while(1){myFunction();}
     
  6. chown33, Dec 6, 2010
    Last edited: Dec 6, 2010

    macrumors 603

    Joined:
    Aug 9, 2009
    #6
    That's how you do it in Objective-C, too.

    If you're asking how to call your function every time through a run-loop, or as fast as possible with a run-loop, then you'll have to clarify by explaining which of those you want.

    Either way, it will not be identical to the while(1) loop. The reason it won't be identical is that a while(1) never pauses or breaks or does anything else. A run-loop may. If that doesn't make sense, then you should first study run-loops more. Then come up with a more precise and more detailed explanation of exactly what should happen, in what order, and at what rate. As given, your description is too vague to determine exactly what you want to happen. In particular, "at the start of each run loop" is unclear.

    In studying run-loops, you should also read about CFRunLoop, not just NSRunLoop. You may also find CFRunLoopObserver to be useful.
    http://developer.apple.com/library/....html#//apple_ref/doc/uid/10000057i-CH16-SW22
    http://developer.apple.com/library/...e/reference.html#//apple_ref/doc/uid/20001442
     
  7. macrumors 68030

    Catfish_Man

    Joined:
    Sep 13, 2001
    Location:
    Portland, OR
    #7

    All C code is also valid Objective-C code.
     
  8. thread starter macrumors member

    Joined:
    Oct 6, 2010
    #8
    I am animating, in an OpenGL context, objects that bounce off each other. The method that I want called repeatedly is the method that updates the location of each object. At the end of the update method is
    Code:
    [self setNeedsDisplay:YES];
    . I want the update method called once every time through the run loop. Now, according to my understanding of run loops, if I stick the code
    Code:
    while(1){update();}
    inside a method that will be called, like, say, "awakeFromNib", then the run loop will not complete because of the infinite loop. I do not want this to happen because I want "drawRect" to be called. In any case, it seems that I should work within the structure run loop.

    These are the details. In short: I want a method called once each time through the run loop. If this request doesn't make sense (and I really think it does), please tell me explicitly what my error is.
     
  9. chown33, Dec 6, 2010
    Last edited: Dec 6, 2010

    macrumors 603

    Joined:
    Aug 9, 2009
    #9
    You're animating. The periodic timer was already correct. Go back to it.

    I don't see what you were hoping to gain by eliminating a timer. It's animation; it's supposed to be periodic.


    EDITED TO ADD:
    The error is in specifying "once each time through the run loop".

    Go read "The Run Loop Sequence of Events":
    http://developer.apple.com/library/....html#//apple_ref/doc/uid/10000057i-CH16-SW22

    Note Step 7 is "Put the thread to sleep until...". Without a periodic timer, how would your animation be regular? Yet if you have a periodic timer, why do you need anything else other than that?

    Also note that the loop may repeat several times very rapidly, if there are multiple pending tasks after a wakeup and expiration isn't reached (Step 9). If your update function is really being called once each time through the run loop, it will be called several times in rapid succession. This will occur at an irregular (untimed) rate. What will that do to the animation?
     
  10. macrumors 601

    HiRez

    Joined:
    Jan 6, 2004
    Location:
    Western US
    #10
    Also, you should probably be using CADisplayLink instead of NSTimer to trigger your animation updates as it is more accurate and synchronized to the display's refresh interval.
     
  11. kainjow, Dec 6, 2010
    Last edited: Dec 6, 2010

    Moderator emeritus

    kainjow

    Joined:
    Jun 15, 2000
    #11
    but then you introduce thread safety issues, which may be a bit much at this point :)

    EDIT: oops, I'm thinking CGDisplayLink.
     
  12. thread starter macrumors member

    Joined:
    Oct 6, 2010
    #12
    chown33, I would appreciate it if you refrained from responding to my posts (seriously). Although you have helped me out at least once, on average you cause the discussion in my posts to degenerate. In particular, I don't want to have to explain why I don't want to use a timer and then argue about it.

    Please, no one has addressed my original question yet.
     
  13. macrumors G5

    gnasher729

    Joined:
    Nov 25, 2005
    #13
    It's not a problem to know nothing, but it is a problem to refuse to learn. You were given the best possible advice, and you ask the person giving it not to respond to your posts?
     
  14. macrumors 68030

    Catfish_Man

    Joined:
    Sep 13, 2001
    Location:
    Portland, OR
    #14
    Actually they have, you just don't understand the answer.

    To answer the question very directly: use a runloop source. An example of a runloop source is a timer. You could also write your own custom CFRunLoopSource that signaled readiness as soon as it fired. That would probably screw up your animation though.

    Probably the simplest way to do this is to use the -performSelector:afterDelay: method and pass 0 for the delay.

    However, that will use a timer internally, which is apparently bad?
     
  15. macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #15
    It makes me sad to reply to someone who is so willing to reject help. chown33 linked to a document that explains very specifically how one would get something to occur at various points in the run loop. Specifically, you could add a run loop observer to the entrance to the run loop (for example). The document chown33 linked to provides a link to:
    http://developer.apple.com/library/...e/reference.html#//apple_ref/doc/uid/20001442

    This is the reference for setting up a run loop observer that will fire when you have requested. Of course, as others have explained, even if you read that and set it up you are not going to get the desired behavior because of the nature of the run loop. It's not doing what you're expecting. People are trying to warn you that the timer is a valid approach and there's no reason to abandon it and you are expressing that you do not want good advice, you want to know how to do the wrong thing. Those documents will tell you, but when you discover that tying into the run loop is not what you want in this case and come back looking for more help, you're going to have a harder time getting any when you are actively discouraging it.

    In any case, good luck with this project and in getting assistance in the future.

    -Lee
     
  16. macrumors regular

    Joined:
    Mar 5, 2004
    #16
    What you perceive as degeneration is simply mass confusion caused by your own posts. You seem unable to fully explain your situation and are too quick to reject the sound advice offered to you.
     
  17. macrumors 603

    Joined:
    Aug 9, 2009
    #17
    You asked for an explanation of what your error was in this:
    I gave an explanation of the error. If you wish to challenge my explanation, or refute it by pointing out errors in my explanation, then please do so. I did double-check the reference docs before writing the explanation, but I could have made a mistake. Otherwise all I can do is refer you to Apple's documentation, and note that the questions I asked are not merely rhetorical devices. They are simple logical questions that arise directly from how a run-loop works. Without a timer of some kind, how do you propose to have regular periodic animated frames? That seems like a fundamental question to me.

    Perhaps you should explain why you don't want to use a timer, given the context of your error. If a run-loop cannot be made to do what you want without using a timer of some kind, then it seems logical to me that the reasons for not using a timer be explained.
     
  18. macrumors 6502

    Joined:
    Dec 17, 2009
    #18
    While I know nothing about animation in Obj-C or Cocoa, I have used a do...while loop in my Obj-C programs, the same way I used them in C++. They work just fine, and the context is exactly the same.
     
  19. macrumors 6502a

    GorillaPaws

    Joined:
    Oct 26, 2003
    Location:
    Richmond, VA
    #19
    The arrogance of that response is appaling. Read this article--at least twice.
     
  20. thread starter macrumors member

    Joined:
    Oct 6, 2010
    #20
    Animating something by updating across irregular time intervals is not a problem: just calculate how much time has elapsed since the last update, and update using that time factor. If I am animating objects that interact with each other by colliding (in a physically realistic way), I may run into trouble if too many interactions occur in a short period of time, for the time needed to calculate the collisions may be longer than the ideal update interval length. In this case I want to compute the time of the collisions and update the locations over the whole sequence of collisions - but draw nothing until everything in the model is current. I will know when everything is current when I check for the next collision and find that it occurs in the (relatively) distant future. It is then that I will do the drawing (after one final update that coordinates the model with system time). I wouldn't want to wait to draw as I may have already waited to draw while sorting out the collisions.

    It seems my answer does lie with CFRunLoopObserver, which is described in a link that was posted earlier:

    http://developer.apple.com/library/mac/#documentation/CoreFoundation/Reference/CFRunLoopObserverRef/Reference/reference.html%23//apple_ref/doc/uid/20001442

    In particular, the "The Run Loop Sequence of Events" is helpful (I tried to look for this earlier, but didn't know what to search for):

    1. Notify observers that the run loop has been entered.
    2. Notify observers that any ready timers are about to fire.
    3. Notify observers that any input sources that are not port based are about to fire.
    4. Fire any non-port-based input sources that are ready to fire.
    5. If a port-based input source is ready and waiting to fire, process the event immediately. Go to step 9.
    6. Notify observers that the thread is about to sleep.
    7. Put the thread to sleep until one of the following events occurs:
    * An event arrives for a port-based input source.
    * A timer fires.
    * The timeout value set for the run loop expires.
    * The run loop is explicitly woken up.
    8. Notify observers that the thread just woke up.
    9. Process the pending event.
    * If a user-defined timer fired, process the timer event and restart the loop. Go to step 2.
    * If an input source fired, deliver the event.
    * If the run loop was explicitly woken up but has not yet timed out, restart the loop. Go to step 2.
    10. Notify observers that the run loop has exited.
     
  21. macrumors 6502a

    Joined:
    May 10, 2009
    Location:
    Des Moines, WA
    #21
    So if I'm reading correctly an apology of some sort seems in order. Are you capable of issuing one?
     
  22. macrumors 68030

    jared_kipe

    Joined:
    Dec 8, 2003
    Location:
    Seattle
    #22
    Do you think this is the first time someone has ever tried to animate and calculate collisions? Technologies like CADisplayLink were created for just this kind of situation. Everything I've ever read about OpenGL uses a "time since last frame" approach to animation, often breaking up the modeling function from the displaying function so you can iterate over the model multiple times if too much time has passed since the last display.

    Seems like maybe a book is more called for than a forum post right now.
     
  23. macrumors 603

    Joined:
    Aug 9, 2009
    #23
    If you had posted that explanation to begin with, we all would have known why you removed the timer. Then I wouldn't have told you to go back to it. And I still would have pointed you to the CFRunLoop and CFRunLoopObserver reference docs.

    You're welcome.
     
  24. Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #24
    Starting to look doubtful...
     
  25. macrumors 6502a

    Joined:
    May 10, 2009
    Location:
    Des Moines, WA
    #25
    Character flaws are hard to overcome.
     

Share This Page