2013-12-15 16 views
5

Ho difficoltà ad aggiornare l'interfaccia utente utilizzando performSelectorOnMainThread. Ecco la mia situazione. Nel mio viewDidLoad ho impostato un indicatore di attività e un'etichetta. Quindi chiamo un selettore per recuperare alcuni dati da un server. Quindi chiamo un selettore per aggiornare l'interfaccia utente dopo un ritardo. Ecco il codice: controlliObiettivo C: problemi durante l'aggiornamento dell'interfaccia utente sul thread principale

- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 
    self.reloadSchools = [[UIAlertView alloc] init]; 
    self.reloadSchools.message = @"There was an error loading the schools. Please try again."; 
    self.reloadSchools.title = @"We're Sorry"; 
    self.schoolPickerLabel = [[UILabel alloc]init]; 
    self.schoolPicker = [[UIPickerView alloc] init]; 
    self.schoolPicker.delegate = self; 
    self.schoolPicker.dataSource = self; 
    self.server = [[Server alloc]init]; 
    schoolList = NO; 

    _activityIndicator = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; 
    [self.view addSubview:_activityIndicator]; 
    [self.view bringSubviewToFront:_activityIndicator]; 
    [_activityIndicator startAnimating]; 

    [NSThread detachNewThreadSelector: @selector(getSchoolList) toTarget: self withObject: nil]; 
    [self performSelector:@selector(updateUI) withObject:nil afterDelay:20.0]; 
} 

Il selettore updateUI per vedere se i dati sono stati recuperati e chiede un selettore sul thread principale per aggiornare l'interfaccia utente di conseguenza. Ecco il codice per queste parti:

-(void)updateUI 
{ 
    self.schools = [_server returnData]; 
    if(!(self.schools == nil)) { 
     [self performSelectorOnMainThread:@selector(fillPickerView) withObject:nil waitUntilDone:YES]; 
    } 
    else { 
     [self performSelectorOnMainThread:@selector(showError) withObject:nil waitUntilDone:YES]; 
    } 
} 

-(void)showError { 
    NSLog(@"show error"); 
    [_activityIndicator stopAnimating]; 
    [self.reloadSchools show]; 
    } 
-(void)fillPickerView { 
    NSLog(@"fill picker view"); 
    schoolList = YES; 
    NSString *schoolString = [[NSString alloc] initWithData:self.schools encoding:NSUTF8StringEncoding]; 
    self.schoolPickerLabel.text = @"Please select your school:"; 
    self.shoolArray = [[schoolString componentsSeparatedByString:@"#"] mutableCopy]; 
    [self.schoolPicker reloadAllComponents]; 
    [_activityIndicator stopAnimating]; 
} 

Quando il selettore fillPickerView è chiamato indicatore di attività continua a girare, il testo dell'etichetta non cambia, e la vista selettore non ricarica suo contenuto. Qualcuno può spiegarmi perché il metodo che sto usando non sta funzionando per aggiornare il mio ui sul thread principale?

+0

è possibile usa [GCD] (http://developer.apple.com/library/ios/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html) per inviare i tuoi dati sull'altro thread e aggiornare l'interfaccia utente nel thread principale, è meglio dell'uso di NSThread. – johnMa

risposta

27
dispatch_async(dispatch_get_global_queue(0, 0), ^{ 
//load your data here. 
dispatch_async(dispatch_get_main_queue(), ^{ 
       //update UI in main thread. 
      }); 
}); 
+0

grazie per il suggerimento – angerboy

0

Prima di tutto non dovresti usare detachNewThreadSelector. Dovresti utilizzare GCD e inviare la tua attività in background a una coda asincrona. I thread sono costosi da creare. GCD svolge un lavoro molto migliore nella gestione delle risorse di sistema.

Ignorandolo, il tuo codice non ha molto senso per me. Si invia un metodo, getSchoolList, per l'esecuzione su un thread in background. Non si mostra il codice che si sta eseguendo in background.

Quindi utilizzare performSelector: withObject: afterDelay per eseguire il metodo updateUI sul thread principale dopo un ritardo fisso di 20 secondi.

updateUI controlla self.schools, che presumibilmente è stato impostato dal thread in background e potrebbe essere o non essere eseguito. Se self.schools È nullo, si chiama fillPickerView utilizzando performSelectorOnMainThread. Questo non ha senso perché se self.schools è nullo, non ci sono dati per riempire il picker.

Se self.schools non è nullo, viene visualizzato un errore, utilizzando nuovamente performSelectorOnMainThread.

Mi sembra che la logica sul controllo di self.schools sia all'indietro. Se è nullo dovresti visualizzare un errore e se NON è nulla, devi riempire il selettore.

Problema successivo: in entrambi i casi si chiama performSelectorOnMainThread: withObject: waitUntilDone: dal thread principale. Chiamare quel metodo dal thread principale non ha senso.

Terzo problema: non ha senso attendere un intervallo di tempo arbitrario per eseguire un'attività in background fino al completamento e quindi avere esito positivo o negativo. Non avrai idea di cosa succederà per tutti e 20 i secondi. Se l'attività in background finisce prima, non lo saprai mai.

Invece, è necessario che l'attività in background venga notificata al thread principale una volta eseguita l'attività. Quello sarebbe un uso valido di performSelectorOnMainThread: withObject: waitUntilDone :, mentre non lo chiama dal thread principale. (Anche in questo caso, però, si dovrebbe refactoring questo codice per utilizzare GCD, non utilizzando direttamente le discussioni.

sembra abbastanza chiaro che ci si trova in sopra la vostra testa. Il codice che hai postato ha bisogno di essere riscritto completamente.

+7

Non sono sopra la mia testa, sono un principiante. Innanzitutto, il mio selettore fillPickerView viene chiamato se le scuole non sono nulle, come indicato da: if (! (Self.schools == nil)). Ovviamente ho bisogno di usare GCD, quindi grazie per il suggerimento. In generale, non penso sia necessario chiamare la gente per essere fuori di testa, le persone sono qui per imparare. – angerboy

+0

Questa è una discussione precedente, ma ho appena visto la tua risposta. Non volevo insultare, e mi scuso se sembrava così. La concorrenza è un argomento avanzato, e ci sono innumerevoli modi in cui puoi spararti ai piedi (più volte contemporaneamente, anche.). Ti consiglio di non immergerti in una programmazione concorrente finché non hai esperienza più che di livello principiante. È come provare a pilotare un assolo del 747 quando stai ancora imparando le basi di un cucciolo di pifferaio. –

Problemi correlati