views:

45

answers:

2

Bear with me, this one is hard to explain. I hope some hero out there knows what’s going on here. Some history needed;

One of my cocoa objects, “Ball” represents a small graphic. It only makes sense within a view. In some of the Ball’s methods, it asks the view to redraw. Most importantly, it asks the view to redraw whenever the Ball’s position parameter is set. This is achieved in the setter.

Here’s the mouthful, as suggested:

In View.m

- (void)mouseUp:(NSEvent *)theEvent {
    if (![runnerPath isEmpty]) {
        [walkPath removeAllPoints];
        [walkPath appendBezierPath:runnerPath];
        [runnerPath removeAllPoints];

        [[self held] setStep:0];
        [[self held] setPath:walkPath];
        [NSTimer scheduledTimerWithTimeInterval:.01 target:[self held] selector:@selector(pace) userInfo:nil repeats:YES];

        }
}

In Ball.m

 - (void)pace { 
    CGFloat juice = 10;
    BOOL loop = YES;

    while (loop) {
        if ([self step] == [[self path] elementCount]) {
            if ([[self timer] isValid]) {
                [[self timer] invalidate];
            }
            [[self path] removeAllPoints];
//          @throw([NSException exceptionWithName:@"test" reason:@"reason" userInfo:nil]);
        }

        if (loop) {
            CGFloat distance;
            NSPoint stepPoint;

            if ([[self path] elementCount] > 0) {
                NSPoint returnPoints[2];
                [[self path] elementAtIndex:[self step] associatedPoints:returnPoints];
                stepPoint = returnPoints[0];
                distance = pixelDistance([self position], stepPoint);
            }

            if (distance <= juice) {
                [self setPosition:stepPoint];
                if (distance < juice) {
                    juice -= distance;
                    loop = YES;
                    [self setStep:[self step]+1];
                } else {
                    loop = NO;
                }
            } else {            
                NSPoint cutPoint = moveAlongBetween([self position], stepPoint, juice);
                [self setPosition:cutPoint];

                loop = NO;
            }

        }
    }

}
+1  A: 

Try calling

for (NSView *each in [self views]) {
    ...
}

I'm assuming that views is an array, so fast enumeration applies to it directly and there is no need to call allObjects.

A couple of other points.

  1. Have you set a Global breakpoint of objc_exception_throw? This will apply to all Xcode projects and is so useful I'm surprised it isn't set by default.
  2. You say you looked at the Console for errors. I take it, then, that you didn't set a breakpoint on the code and step into it to see exactly what is happening when your execution reaches that point? Have a look at the Xcode Debugging Guide
Abizern
+1 He says `views` is an NSSet, so fast enumeration applies there too.
David Gelhar
How has this anything to do with the app freeze?
mvds
I haven’t done the breakpoint thing. How would it help?
Not Rick Astley
@NRA - If you set a breakpoint and step into the code, you will be able to see the state of local and global variables and find out where your hang is occurring. I've added a link to my answer.
Abizern
+1  A: 

could you also tell how you handle exceptions? since normally an unrecognized selector will end your program. Maybe you need an exception rather than an unrecognized selector. Try:

@throw([NSException exceptionWithName:@"test" reason:@"reason" userInfo:nil]);

If this would fix it as well, you're doing something after this code which freezes the app.

edit: thanks for the code update.

There's some weird stuff going on here! I'm not going to rewrite the whole thing, so here's some pointers:

  • first of all: you're looping inside some routine that is called from a timer loop. Is that intended? There is no way to pause execution within that while() loop, so it will happen in a blink anyway. You would need to keep some state information in the class. E.g. adding a loop counter every time pace is called.
  • second: if you start a timer, it will call your selector with the timer as an argument. So define the function as -(void)pace:(NSTimer*)timer, and use timer, not [self timer] (the latter will not be your timer anyway, if you don't assign it!)
  • third: you're firing 100 times a second. That is a lot, and presumably higher than the refresh rate of any device you're writing this for. I think 20/sec is enough.
  • fourth: to be sure, if you change it to -(void)pace:(NSTimer*)timer, don't forget to use @selector(pace:) (i.e. don't forget the :)

fix those things, and if it's still broken, update your question again and put in comment so we will know. Good luck!

mvds
The @throw statement also does it. I’m a bit of a novice, but I take it I should look for the error further down the line? Thanks for your help.
Not Rick Astley
Ok then there are 2 options: 1) you bail out in time to not freeze the app further down the road 2) your exception handling does something else which prevents freezing. Luckily there's a great way to bail the function out *without* the exception, called `return;`. If the code doesn't freeze then, it must be something past this point in the same function. If it does freeze, you either depend on some magic happening in the exception handling, *or* there's something buggy happening past the point where you call this function from, before handling the exception.
mvds
but this is getting a little out of hand. To cut things short: are you very sure you're not setting `self.position = ...;` from within the setter? Please `NSLog("something");` in `setPosition` to see if you're not in an infinite loop.
mvds
(or, as an alternative: you're not doing `self.something = ...;` which does `self.position = ...;`, introducing a loop?)
mvds
Sweet — now that I used the timer from the argument, everything works without the exception. I added a return; to make it all neater.The weird loop is intended, and uses as loop counter the “step” ivar. This is of course not very elegant, but the code is really just a prototype, and the interesting bit is elsewhere. The same goes for the overambitious firing interval — I’ve been messing around with the values to see if some interface things can keep up.Anyway, great help. Every time I ask things here, it ends up brightening the day. Can’t thank you enough.
Not Rick Astley
Btw, I used to assign the timer to an ivar, but got rid of that. Yeah, that was stupid, and probably what got me into this mess.
Not Rick Astley
@Rick: now I see, a `[self setStep:[self step]+1];`, buried deep down. Why not make it an int and just `step++`? anyway, glad I could be of help!
mvds