2010-04-06 10 views
18

Próbuję użyć osobnego wątku do pracy z niektórymi API.iPhone: jak korzystać performSelector: onThread: withObject: waitUntilDone: method?

Problem polega na tym, że nie jestem w stanie użyć metody performSelector:onThread:withObject:waitUntilDone: z wątkiem, który stworzyłem dla tego.

Mój kod:

@interface MyObject : NSObject { 
    NSThread *_myThread; 
} 
@property(nonatomic, retain) NSThread *myThread; 
@end 

@implementation MyObject 
@synthesize myThread = _myThread; 
- (NSThread *)myThread { 
    if (_myThread == nil) { 
    NSThread *myThreadTemp = [[NSThread alloc] init]; 
    [myThreadTemp start]; 
    self. myThread = myThreadTemp; 
    [myThreadTemp release]; 
    } 
    return _myThread; 
} 

- (id)init { 
    if (self = [super init]) { 
    [self performSelector:@selector(privateInit:) onThread:[self myThread] withObject:nil waitUntilDone:NO]; 
    } 
    return self; 
} 
- (void)privateInit:(id)object { 
    NSLog(@"MyObject - privateInit start"); 
} 

- (void)dealloc { 
    [_myThread release]; 
    _myThread = nil; 
    [super dealloc]; 
} 
@end 

"MyObject - privateInit start" nigdy nie zostanie wydrukowany.
Czego mi brakuje?

Próbowałem utworzyć instancję wątku z elementem docelowym i selektorem, próbowałem czekać na zakończenie wykonywania metody (waitUntilDone:YES).
Nic nie pomaga.

AKTUALIZACJA:
Nie potrzebuję tej wielowątkowości do oddzielania kosztownych operacji na inny wątek.
W tym przypadku można użyć performSelectorInBackground, jak wspomniano w kilku odpowiedziach.
Głównym powodem tego osobnego wątku jest potrzeba wykonania wszystkich czynności w API (TTS przez Loquendo) z jednego wątku.
To znaczy, że muszę tworzyć instancję obiektu TTS i wywoływać metody na tym obiekcie z tego samego wątku przez cały czas.

Odpowiedz

14

Znalazłem odpowiedź!

W celu utrzymania wątek w górę, istnieje potrzeba dodatkowego kawałka kodu:

- (void)threadMain:(id)data { 
    NSAutoreleasePool *pool = [NSAutoreleasePool new]; 

    NSRunLoop *runloop = [NSRunLoop currentRunLoop]; 
    [runloop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode]; 

    while (isAlive) { // 'isAlive' is a variable that is used to control the thread existence... 
     [runloop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; 
    } 

    [pool release]; 
} 


a następnego wiersza:

NSThread *myThreadTemp = [[NSThread alloc] init]; 

należy zastąpić ten jeden:

NSThread *myThreadTemp = [[NSThread alloc] initWithTarget:self selector:@selector(threadMain:) object:nil]; 

EDYTOWANIE: Jak zasugerował kilka ludzie tutaj dodałem kilka linii kodu (NSAutoreleasePool, metoda addPort i boja "isAlive").

+1

W ten sposób nie tworzysz nieskończonej pętli? Jeśli runloop nie ma nic do zrobienia, po prostu zniknie natychmiast i wszystko, co musisz zrobić, to wywołanie '- run' tak często, jak twój procesor może sobie poradzić. (Wykorzystanie procesora w 100%) – bastibe

+0

Infinite - yes. Ale pętla nie jest powtarzana przez cały czas. Wrzuciłem linię dziennika (NSLog ...) w czasie i została ona wywołana tylko raz ... –

+0

Jest to nieoczekiwane zachowanie i nie należy polegać na nim. Uruchomiona pętla bez skonfigurowanych źródeł wejściowych lub timerów powinna natychmiast wyjść, dlatego też pętla powinna działać nieprzerwanie w kółko, wykorzystując 100% procesora, jak powiedział plakat. Więcej informacji można znaleźć w dokumentach nsrunloop. –

1

Utworzono wątek, ale nie jest uruchomiony. Musi działać, aby coś wykonać.

Możesz również użyć "performSelectorInBackground". Będzie on w kolejce wywoływał wywołanie, dopóki inicjalizacja nie zostanie wykonana.

+0

Dziękuję za komentarz. '[myThreadTemp start];' nie powinien sprawiać, że wątek działa? 'performSelectorInBackground' nie jest dla mnie dobry. Muszę użyć jednego wątku dla wszystkich operacji na tym API, którego używam (TTS od Loquendo). –

+1

W czasie między '[myThreadTemp start];' i twoim selektorem wydajności, wątek mógł zostać zatrzymany. – Giao

+0

Jeśli potrzebujesz osobnego wątku, możesz użyć "detachNewThreadSelector: toTarget: withObject:". Tworzy i odradza nowy wątek, który rozpocznie się natychmiast, więc nie musisz przejmować się tworzeniem NSThread. –

4

Cóż, przypuszczam, że mam lepsze rozwiązanie

- (void)run{ 
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 
    NSRunLoop *runLoop = [NSRunLoop currentRunLoop]; 
    running = true; 
    [[NSRunLoop currentRunLoop] addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode]; 
    while (running && [runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]){ 
     //run loop spinned ones 
    } 

    [pool release]; 
} 

Co ja tu robię?
1) Dodanie portu próbnego w tym miejscu jako źródła uniemożliwi bezproblemowe wyjście z trybu runMode:beforeDate:.
2) Metoda runMode:beforeDate: blokuje wątek, aż pojawi się coś w runLoop.

+0

Jeśli wywołanie performSelector: onThread: kilka razy, czy wszystkie moje kolejki selektora w górę? – onmyway133

12

To jest to, co działa dla mnie.Główna pętla zaczerpnięte z dokumentacji Apple http://developer.apple.com/iphone/library/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html#//apple_ref/doc/uid/10000057i-CH16-SW25

- (void) start { 
    self.imageSaverThread = [[[NSThread alloc] initWithTarget:self selector:@selector(imageSaverThreadMain) object:nil] autorelease]; 
    [self.imageSaverThread start]; 
} 

- (void) imageSaverKeepAlive { 
    [self performSelector:@selector(imageSaverKeepAlive) withObject:nil afterDelay:60];  
} 

- (void)imageSaverThreadMain 
{ 
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 

    // Add selector to prevent CFRunLoopRunInMode from returning immediately 
    [self performSelector:@selector(imageSaverKeepAlive) withObject:nil afterDelay:60]; 
    BOOL done = NO; 

    do 
    { 
     NSAutoreleasePool *tempPool = [[NSAutoreleasePool alloc] init]; 
     // Start the run loop but return after each source is handled. 
     SInt32 result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 10, YES); 

     // If a source explicitly stopped the run loop, or if there are no 
     // sources or timers, go ahead and exit. 
     if ((result == kCFRunLoopRunStopped) || (result == kCFRunLoopRunFinished)) 
      done = YES; 

     [tempPool release]; 
    } 
    while (!done); 

    [pool release]; 
} 

Nadzieja pomaga

+0

Teraz to jest pomoc. Zwróć uwagę, jak należy dodać jakieś wydarzenie, aby zapobiec natychmiastowemu zwracaniu się pętli uruchamiania. –

Powiązane problemy