2011-12-21 12 views
14

Mam obiekt NSRunLoop, do którego załączam timery i strumienie. Działa świetnie. Powstrzymanie to zupełnie inna historia.CFRunLoopRun() vs [NSRunLoop run]

Uruchomię pętlę, używając [runLoop run].

Jeśli spróbuję zatrzymać pętlę za pomocą CRunLoopStop([[NSRunLoop currentRunLoop] getCFRunLoop]), pętla się nie zatrzyma. Jeśli zamiast tego uruchomię pętlę, używając CRunLoopRun(), to działa. Upewniłem się również, że wywołanie odbywa się na poprawnym wątku (tym, który uruchamia moją niestandardową pętlę uruchamiania). Mam debugowane to z pthread_self().

Znalazłem archiwum listy adresowej, w którym programista powiedział: "nie przejmuj się używaniem CRunLoopStop(), jeśli uruchomiłeś pętlę, korzystając z metody uruchamiania NSRunLoop". Mogę zrozumieć, dlaczego tak właśnie jest - zazwyczaj parujesz inicjalizatory i finalizatory z tego samego zestawu funkcji.

Jak zatrzymać NSRunLoop bez "uciekania się do CF"? Nie widzę metody stop na NSRunLoop. Docs mówi, że można zatrzymać pętlę run na trzy sposoby:

  1. Skonfiguruj pętli run uruchomić o wartości limitu czasu
  2. Powiedz pętlę run przestać używać CFRunLoopStop()
  3. Usuń wszystkie źródła wejściowe, ale to niewiarygodny sposób na zatrzymanie pętli, ponieważ nigdy nie wiadomo, co utknęło w pętli za plecami.

Cóż, już próbowałem 2. i jest to "brzydkie" uczucie, ponieważ musieć kopać do CF. 3. nie wchodzi w rachubę - nie podoba mi się niedeterministyczny kod.

To daje nam 1. Jeśli rozumiem dokumenty poprawnie, nie można "dodać" limitu czasu do już istniejącej pętli uruchamiania. Możesz uruchamiać tylko nowe pętle uruchamiania z limitem czasu. Jeśli uruchomię nową pętlę uruchamiania, nie rozwiąże ona mojego problemu, ponieważ utworzy ona zagnieżdżoną pętlę uruchamiania. Wciąż wskakuję z powrotem do starego, tego samego chciałem zatrzymać ... prawda? Mogłem źle to zrozumieć. Ponadto nie chcę uruchamiać pętli z wartością limitu czasu. Jeśli to zrobię, będę musiał dokonać kompromisu pomiędzy spalaniem cykli procesora (mała wartość limitu czasu) a czasem reakcji (wysoka wartość limitu czasu).

Jest to konfiguracja mam teraz (pseudo kod-owski):

Communicator.h

@interface Communicator : NSObject { 
    NSThread* commThread; 
} 

-(void) start; 
-(void) stop; 
@end 

Communicator.m

@interface Communicator (private) 
-(void) threadLoop:(id) argument; 
-(void) stopThread; 
@end 

@implementation Communicator 
-(void) start { 
    thread = [[NSThread alloc] initWithTarget:self 
            selector:@selector(threadLoop:) 
             object:nil]; 
    [thread start]; 
} 

-(void) stop { 
    [self performSelector:@selector(stopThread) 
       onThread:thread 
       withObject:self 
      waitUntilDone:NO]; 
    // Code ommitted for waiting for the thread to exit... 
    [thread release]; 
    thread = nil; 
} 
@end 

@implementation Communicator (private) 
-(void) stopThread { 
    CRunLoopStop([[NSRunLoop currentRunLoop] getCFRunLoop]); 
} 

-(void) threadLoop:(id) argument { 
    // Code ommitted for setting up auto release pool 

    NSRunLoop* runLoop = [NSRunLoop currentRunLoop]; 

    // Code omitted for adding input sources to the run loop 

    CFRunLoopRun(); 
    // [runLoop run]; <- not stoppable with 

    // Code omitted for draining auto release pools 

    // Code omitted for signalling that the thread has exited 
} 
@endif 

Czym jestem do zrobienia? Czy jest to powszechne/dobry wzór, aby poradzić sobie z CF? Nie znam wystarczająco dobrze Fundacji. Czy ingerencja w warstwę CF jest potencjalnie niebezpieczna (w odniesieniu do uszkodzenia pamięci, niespójności, wycieków pamięci)? Czy istnieje lepszy wzór do osiągnięcia tego, co próbuję osiągnąć?

Odpowiedz

7

Dobrze ci idzie. Nie ma problemu z używaniem CoreFoundation, gdy nie możesz osiągnąć swojego celu z Fundacją. Ponieważ CoreFoundation to C, łatwiej jest zepsuć zarządzanie pamięcią, ale nie ma żadnego wewnętrznego niebezpieczeństwa związanego z używaniem CFRunLoop, a nie NSRunLoop (sometimes it may even be safer: API są wątkowo bezpieczne, podczas gdy NSRunLoop nie jest).

Jeśli chcesz zatrzymać swój NSRunLoop, możesz go uruchomić, używając runMode:beforeDate:. runMode:beforeDate: powraca natychmiast po przetworzeniu źródła wejściowego, dzięki czemu nie trzeba czekać aż do osiągnięcia limitu czasu.

NSRunLoop *runLoop = [NSRunLoop currentRunLoop]; 
NSDate *date = [NSDate distantFuture]; 
while (!runLoopIsStopped && [runLoop runMode:NSDefaultRunLoopMode beforeDate:date]); 

Następnie, aby zatrzymać pętlę biegu, wystarczy ustawić runLoopIsStopped do YES.

+0

Ah, sprytnie! Nie widziałem, aby 'runMode' działał tylko raz. Czy występuje znaczna kara za wydajność spowodowana ponownym uruchomieniem pętli? –

+0

Nie, nie ma: tak działają '- [Uruchom NSRunLoop]' i '- [NSRunLoop runUntilDate:]'. –

+1

Wątek nie zatrzyma się, jeśli ustawisz runLoopIsStopped na YES. Musisz ustawić to na TAK i wykonać akcję na tym wątku programu runloop, w przeciwnym razie [runLoop runMode: NSDefaultRunLoopMode beforeDate: date] nie zostanie zakończony. To zajęło mi wieki, aby dowiedzieć się. – Pada