【发布时间】:2014-08-09 15:59:02
【问题描述】:
我编写了一个程序来播放歌曲队列,这些歌曲在后台任务中使用委托进行处理。我将回复发送回AppController 以移动NSSlider 显示正在播放的歌曲的进度并删除刚刚播放的NSCollection View 实体(歌曲)。大多数时候效果很好,但在两三首歌之后;实体消失但其余对象不会重新排列(在屏幕上向上移动),直到我使用鼠标或键盘使主屏幕成为焦点。我对其他类似问题使用了几个建议,并且通常得到相同的响应。我现在使用的代码:
-(void) songPlayer:(SongPlayer *)player removeEntity:(DesktopEntity *)entity {
int i = 0;
NSIndexSet *indexes;
indexes = [NSIndexSet indexSetWithIndex:i];
NSLog(@"about to try");
@try {
// [_arrayController removeObjectAtArrangedObjectIndex:0];
[_seqFile removeObjectAtIndex:0];
[_songs removeObjectAtIndex:0];
[_myCollectionView setContent:_songs];
[_arrayController rearrangeObjects];
[_myCollectionView setNeedsDisplay:YES];
// [_arrayController rearrangeObjects];
// [_window makeKeyAndOrderFront:self];
// [_myCollectionView setNeedsDisplay:YES];
// [self.seqFile reloadData];
}
@catch (NSException *exception) {
NSLog(@"Ugly Ending Caught");
}
@finally {
NSLog(@"Each Time");
}
}
我留下了注释代码,以显示我尝试过的其他事情以获得相同的结果。我相信它必须与刷新 NSView 相关,但不确定如何。
我的线程设置如下:
songPlayer.h
@protocol SongPlayerDelegate;
@interface SongPlayer : NSObject <SliderDelegate> {
id<SongPlayerDelegate> __unsafe_unretained delegate;
}
@property (nonatomic, assign) id<SongPlayerDelegate> delegate;
…
@end
@protocol SongPlayerDelegate <NSObject>
- (void)songPlayer:(SongPlayer *)player removeEntity:(id)enity;
@end
歌曲播放器.M
#import "SongPlayer.h"
@implementation SongPlayer
@synthesize delegate;
-(id) init {
if (self = [super init]) {
}
...
return self;
}
-(id) playMyQueue:(NSMutableArray *)q {
//playsong stuff
…
[self.delegate songPlayer:self removeEntity:entity];
}
appController.h
#import "SongPlayer.h"
@interface AppController : NSObject <SongPlayerDelegate>
@property (weak) IBOutlet NSCollectionView *myCollectionView;
@property IBOutlet NSArrayController *arrayController;
@property (strong) NSMutableArray *songs;
@property SongPlayer *myPlayer;
- (void)songPlayer:(SongPlayer *)player removeEntity:(id)enity;
appController.m
#import "AppController.h"
@implementation AppController
@synthesize songs = _songs;
-(void) awakeFromNib {
_songs = [[NSMutableArray alloc] init];
[_arrayController addObject:s];
…
_myPlayer = [[SongPlayer alloc] init];
…
}
-(IBAction)previewSong:(id)sender {
if ([_myPlayer qActive]) {
[(SongPlayer *)_myPlayer stopQueue];
[_songLabel setStringValue:@"playback has been stopped"];
} else {
if (![_myPlayer isDone]) {
[(SongPlayer *)_myPlayer stopSong];
[_songLabel setStringValue:@"playback has been stopped"];
} else {
[_queue removeAllObjects];
_myPlayer.delegate = self;
NSMutableArray *activeQueue =
[[NSMutableArray alloc] initWithArray:_seqFile];
[_myPlayer performSelectorInBackground:@selector(playMyQueue:)
withObject:activeQueue];
}
}
}
-(void) songPlayer:(SongPlayer *)player removeEntity:(DesktopEntity *)entity {
int i = 0;
NSIndexSet *indexes;
indexes = [NSIndexSet indexSetWithIndex:i];
NSLog(@"about to try");
@try {
[_arrayController removeObjectAtArrangedObjectIndex:0];
[_seqFile removeObjectAtIndex:0];
}
@catch (NSException *exception) {
NSLog(@"Ugly Ending Caught");
}
@finally {
NSLog(@"Each Time");
}
}
【问题讨论】:
-
您不得尝试从后台线程更新 GUI——包括修改绑定到 GUI 的任何模型或控制器属性。所有这些修改都必须在主线程上完成。
-
感谢您的回复。这就是我使用代表的原因。 removeEntity 方法在主线程的 AppController 中。当后台程序通知歌曲完成时执行。
-
委托不解决任何线程问题。 “AppController”不是主线程“的”。对象并不存在于特定线程上。如果您有在后台线程上运行的代码,并且它正在调用方法而不使用特定于线程的 API,例如
-performSelectorOnMainThread:...或带有dispatch_get_main_queue()的 GCD 专门在主线程上执行此操作,那么该方法调用发生在后台线程也是。 -
Ken,对术语感到抱歉,我对 Objective C Cocoa 没有我应有的熟悉。我在做performSelectorOnMainThread... 代码设置如下:
-
您使用的是
-performSelectorInBackground:,而不是OnMainThread。这与我所说的相反。您在后台运行一些工作,只要它不与 GUI 交互就可以了。如果有一些东西需要更新 GUI,那么你需要将它分流到主线程。
标签: objective-c macos delegates nscollectionview