문제

In AppDelegate I have an NSMutableArray with a set of classes that are processed in a background method (songs being played). That part of the Application is working fine. I setup the delegate method and receive status on the work at the conclusion of each item of the array. I wanted to add an NSSlider to display the status of each array item being processed. I tested the slider on a single entity from within the AppDelegate and got the results I expected. The problem is when I’m processing the queue the NSTimer is not firing until the unit of work is completed as if delayed. I suspect I’m not scheduling it properly (because the timer worked on a single song with the same code from within AppDelegate).

AppDelegate.h

@interface AppDelegate : NSObject <NSApplicationDelegate, NSTableViewDataSource,
    NSTableViewDelegate, NSSharingServiceDelegate,
    NSSharingServicePickerDelegate, SongPlayerDelegate>

-(void) songPlayer:(SongPlayer *)player didSomething:(DesktopEntity *)entity;
-(void) songPlayer:(SongPlayer *)player updateSlider:(double)result;

ApplDelegate.m

-(void)previewQueue:(DesktopEntity *)entity {
    …
// schedule work
    _queuePlayer = [[SongPlayer alloc] init];
    _queuePlayer.delegate = self;
    [_queuePlayer performSelectorInBackground:@selector(playMyQueue:) withObject:_queue];
}

SongPlayer.h

@protocol SongPlayerDelegate;
@interface SongPlayer : NSObject {
id<SongPlayerDelegate> __unsafe_unretained delegate;
}
@property (nonatomic, assign) id<SongPlayerDelegate> delegate;
-(id)playMyQueue:NSMutableArray;
@end

@protocol SongPlayerDelegate <NSObject>
- (void)songPlayer:(SongPlayer *)player didSomething:(id)enity;
- (void)songPlayer:(SongPlayer *)player updateSlider:(double)result;
@end
-(id) playMyQueue:(id)entity {
…

    NSDictionary *dict = [(DesktopSoundEntity *)entity tagDictionary];
    NSString *dur = [dict objectForKey:kTagDuration];
    _songDurration = [dur doubleValue];
    _then = [NSDate date];
    if (_myTimer) {
        [_myTimer invalidate];
        _myTimer = nil;
    }
    _myTimer = [NSTimer scheduledTimerWithTimeInterval:0.75 target:self
            selector:@selector(updateTimer:) userInfo:nil repeats:YES];

    [self playSong];
    [self passBackURL:entity];
    return nil;
}
- (void)updateTimer:(NSTimer *)timer {
    double now = ([_then timeIntervalSinceNow]*-1);
    NSLog(@"now-> %f", now);
    double sec = (now / _songDurration) *100;
    NSLog(@"now-> %f", sec);
    if (sec > 100) {
        [_myTimer invalidate];
        _myTimer = nil;
    }
    NSLog(@"SLIDER SHOULD BE UPDATING");
    [self.delegate songPlayer:self updateSlider:sec];
}

The slider isn’t updated until the playMyQueue method completes, I see the bar jump and I get the NSLog message. Any suggestions would be appreciated.

도움이 되었습니까?

해결책

Timers won't fire unless they are scheduled in a thread with a running run loop. performSelectorInBackground:.. makes no such guarantee that the work will be scheduled in a thread with a runloop and, in any case, as soon as the invoked selector exits, the thread may be reaped.

To fix, move that timer to a thread with a running run loop. The main thread is likely the best choice given that you are mucking w/UI bits.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top