2012-02-24 3 views
1

У меня есть UILabel и UIProgressView, для которых я даю несколько обновлений, но ничего не отображается, пока мой объект связи не будет завершен.UILabel и UIProgressView не обновляются до тех пор, пока мой класс связи не завершится

Я получил эту ViewController:

@interface UpdateServerViewController : UIViewController <TaskController> { 
    AgentAssetManager * agentAssetManager; 
} 

@property (strong, nonatomic) IBOutlet UIButton * updateNowButton; 
@property (strong, nonatomic) IBOutlet UILabel * statusLabel; 
@property (strong, nonatomic) IBOutlet UILabel * lastUpdateSuccessTimeLabel; 
@property (strong, nonatomic) IBOutlet UILabel * lastUpdateAttemptTimeLabel; 
@property (strong, nonatomic) IBOutlet UILabel * lastUpdateResultLabel; 
@property (strong, nonatomic) IBOutlet UIProgressView * progressView; 

- (IBAction) updateNow:(id) sender; 
- (void) updateLabels; 
- (void) scheduleInNextRunloopSelector:(SEL)selector; 
- (void) taskSetStatus:(NSString *)status; 
- (void) taskFinishedSuccessfully; 
- (void) taskFinishedUnSuccessfully; 

@end 

Метод updateNow вызывает AgentAssetManager:

[self scheduleInNextRunloopSelector:@selector(updateTime:)]; 

McDbg(m, d+1, @"Calling AgentAssetManager sendToServer"); 
// [statusLabel setText:@"Contacting Server"]; 
[agentAssetManager sendToServer:true taskController:self]; 

AgentAssetManager sendToServer это выполняет ряд шагов, и отправляет уведомления обратно UpdateServerViewController по протоколу TaskController :

- (void) sendToServer:(Boolean) notifyUser 
    taskController:(id<TaskController>) taskCtlr 
{ 
char *   m = "AgentAssetManager.sendToServer"; 
int    d = 0; 
int    steps = 6; // Total number of steps 

McDbg(m, d, @"Send Asset data to server"); 
McDbg(m, d+1, @"Notify User about status/errors=%s", 
     (notifyUser) ? "YES" : "NO"); 

if (taskCtlr != nil) { 
    [taskCtlr taskSetProgress:(float)1/(float)steps]; 
    [taskCtlr taskSetStatus:@"Checking if server is reachable"]; 
} 

McDbg(m, d+1, @"SLEEPING"); 
sleep(5); // XXX tmp debug 

ServerStatusManager *statusMgr = [[ServerStatusManager alloc] init]; 
Boolean ready = [statusMgr isServerReady]; 
McDbg(m, d+1, @"Server Status ready=%s", (ready) ? "YES" : "NO"); 
if (!ready) { 
    NSString *msg = @"Server could not be reached"; 
    McLogWarn(m, @"Server Not ready %@", [statusMgr serverUrlBase]); 
    [self updateResult:false]; 
    if (notifyUser) 
     [McUiError showMessage:msg]; 
    if (taskCtlr) { 
     [taskCtlr taskSetStatus:msg]; 
     [taskCtlr taskFinishedUnSuccessfully]; 
    } 
    return; 
} 

McDbg(m, d+1, @"Getting Asset data"); 
if (taskCtlr != nil) { 
    [taskCtlr taskSetProgress:(float)2/(float)steps]; 
    [taskCtlr taskSetStatus:@"Gathering data"]; 
} 

... etc .... 

Вот UpdateServerViewController taskSetProgress и taskSetStatus:

- (void) taskSetStatus:(NSString *) status 
{ 
char *   m = "UpdateServerViewController.taskSetStatus"; 
int    d = 0; 

McDbg(m, d, @"Status=<%@>", status); 
[statusLabel setText:status]; 
McDbg(m, d+1, @"Finished"); 
} 

- (void) taskSetProgress:(float) percent 
{ 
[progressView setHidden:false]; 
[progressView setProgress:percent animated:YES]; 
} 

Выход отладки ясно показывает, что UpdateServerViewController taskSetProgress и taskSetStatus называются, когда они предполагают, чтобы. Проблема в том, что новые значения набора не вступают в силу до тех пор, пока не будет завершен весь метод agentAssetManager.updateNow.

Основываясь на результатах поиска на этом сайте для подобных проблем UILabel я добавил метод таймера:

- (void) scheduleInNextRunloopSelector:(SEL)selector 
{ 
char *   m = "UpdateServerViewController.scheduleInNext"; 
int    d = 0; 

McDbg(m, d, @"Starting"); 
NSDate *fireDate = [[NSDate alloc] initWithTimeIntervalSinceNow:0.5]; // 500 ms 
NSTimer *timer = [[NSTimer alloc] 
        initWithFireDate:fireDate interval:0.5 target:self 
        selector:selector userInfo:nil repeats:YES]; 

[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode]; 

} 

- (void)updateTime:(NSTimer *)timer 
{ 
char *   m = "UpdateServerViewController.updateTime"; 
int    d = 0; 

McDbg(m, d, @"Starting"); 
[statusLabel setText:@"XXX Timer called"]; 
} 

Проблема заключается в то же самое с или без таймера. С помощью таймера отладка показывает, что таймер запланирован до вызова agentAssetManager.updateNow, но метод updateTime не вызывается до тех пор, пока не завершится действие agentAssetManager.updateNow. Это даже со сном (5) в updateNow, чтобы попытаться избежать состояния гонки нитей.

Отладка показывает, что все средства UpdateServerViewController и AssetAgentManager работают в потоке 1. Я не делаю никаких NSRunLoop, кроме указанного выше таймера.

Должно быть, я пропустил что-то основное. Любая помощь будет действительно оценена!

+0

Что вы используете, чтобы сделать реальную связь между сервером? Если вы используете какой-либо из методов ___withContentsOfURL, они заблокируют основной поток, и никаких обновлений пользовательского интерфейса не произойдет. – jsd

+0

Я использую RestKit для связи с сервером. –

ответ

0

Обязательно сделайте какие-либо изменения ui в основном потоке.

Вы можете попробовать что-то вроде этого:

dispatch_sync(dispatch_get_main_queue(), ^{ 
     [statusLabel setText:@"XXX Timer called"]; 
    } 
); 
+0

Если я использую dispatch_sync(), как вы предлагаете, вся цепочка зависает/блокирует. Очень странно. Я начинаю думать, что RestKit (запускается из AgentAssetManager) делает что-то за кулисами. –