9

I'm just trying to close an NSPanel after a couple second delay, but I can't get my NSTimer to start. It will fire if I explicitly call the fire method on it, but it will never go by itself. Here's my code:

   - (void)startRemoveProgressTimer:(NSNotification *)notification {
    NSLog(@"timer should start");
    timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(removeProgress:) userInfo:nil repeats:NO];
}

- (void)removeProgress:(NSTimer *)timer {
    [progressPanel close];
}

I do have some threading in my code as such. I assume this is what's messing my timer up.

-(void)incomingTextUpdateThread:(NSThread*)parentThread {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

//mark the thread as running
readThreadRunning = TRUE;

const int BUFFER_SIZE = 100;
char byte_buffer[BUFFER_SIZE]; //buffer for holding incoming data
int numBytes = 0; //number of bytes read
NSString *text; //incoming text from the serial port

[NSThread setThreadPriority:1.0];

//this will loop until the serial port closes
while (TRUE) {
    //read() blocks until some data is available or the port is closed
    numBytes = read(serialFileDescriptor, byte_buffer, BUFFER_SIZE);
    if(numBytes > 0) {
        //creat a string from the incoming bytes
        text = [[[NSString alloc] initWithBytes:byte_buffer length:numBytes encoding:[NSString defaultCStringEncoding]] autorelease];
        if(!([text rangeOfString:SEND_NEXT_COORDINATE].location == NSNotFound)) {
            //look for <next> to see if the next data should be sent
            if(coordinateNum <[coordinatesArray count]) {
                [self sendNextCoordinate]; //send coordinates
            }
            else {
                [self writeString:FINISH_COORDINATES_TRANSMIT]; //send <end> to mark transmission as complete
                NSNumber *total = [NSNumber numberWithUnsignedInteger:[coordinatesArray count]];
                NSDictionary *userInfo = [NSDictionary dictionaryWithObject:total forKey:@"progress"];
                [[NSNotificationCenter defaultCenter] postNotificationName:@"uploadProgressChange" object:self userInfo:userInfo]; //update progress bar to completed
            }


        }

        [self performSelectorOnMainThread:@selector(appendToIncomingText:) withObject:text waitUntilDone:YES]; //write incoming text to NSTextView
    } else {
        break; //Stop the thread if there is an error
    }
}

// make sure the serial port is closed
if (serialFileDescriptor != -1) {
    close(serialFileDescriptor);
    serialFileDescriptor = -1;
}

// mark that the thread has quit
readThreadRunning = FALSE;

// give back the pool
[pool release];
}

Which is called from another method by: [self performSelectorInBackground:@selector(incomingTextUpdateThread:) withObject:[NSThread currentThread]];

Bill the Lizard
  • 398,270
  • 210
  • 566
  • 880
strange quark
  • 5,205
  • 7
  • 41
  • 53
  • Hmm... don't see anything wrong, but why are you passing the currentThread as the withObject: parameter? You don't seem to use it in that method, why not just pass nil? – jakev Feb 06 '11 at 05:54
  • 7
    A suspicion I have (just a suspicion, since I can't see what calls startRemoveProgressTimer:) scheduledTimerWithTimeInterval adds the timer to the current thread's run loop, not the main thread's. If you're creating your timer in a background thread that never uses its run loop -- if just sits and spins in a while(1) loop reading from a file descriptor, say -- the run loop never gets a chance to deal with any queued-up timers. Try explicitly adding it to the main thread's run loop instead and see what happens. – rgeorge Feb 06 '11 at 06:18
  • Would it make a difference if I told you that startRemoveProgressTimer: is tied to a NSNotification? As to why I am passing in current thread, I'm not sure. I'm trying to adapt somebody else's code to my application. The original code is here: http://arduino.cc/playground/Interfacing/Cocoa but I had to make some modifications to get it to compile on (Snow) Leopard, and I put in some of my own logic. – strange quark Feb 06 '11 at 06:24
  • I think there's something wrong with your notifications. Can you post your notification code as well, please? (There is no reason that if `startRemoveProgressTimer` is called that your timer should not fire) – aqua Feb 06 '11 at 08:54

1 Answers1

18

Thank you rgeorge!!

Adding the timer to the run loop manually made it work!

timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(removeProgress:) userInfo:nil repeats:NO];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
strange quark
  • 5,205
  • 7
  • 41
  • 53
  • 4
    Specifically, adding it to the *main* (main thread's) run loop. The `scheduledTimer…` methods add the timer to the run loop, but they add it to the current (current thread's) run loop. It would also have worked to do a main-thread perform that you respond to by creating/scheduling the timer. – Peter Hosey Feb 06 '11 at 19:59