iPhone: как использовать executeSelector: onThread: withObject: waitUntilDone: метод? - PullRequest
18 голосов
/ 06 апреля 2010

Я пытаюсь использовать отдельный поток для работы с некоторым API.

Проблема в том, что я не могу использовать метод performSelector:onThread:withObject:waitUntilDone: с потоком, для которого я создан.

Мой код:

@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" никогда не печатается.
Чего мне не хватает?

Я пытался создать экземпляр потока с целью и селектором, пытался дождаться завершения выполнения метода (waitUntilDone:YES).
Ничего не помогает.

UPDATE:
Мне не нужна эта многопоточность для разделения дорогостоящих операций на другой поток.
В этом случае я мог бы использовать performSelectorInBackground, как упоминалось в нескольких ответах.
Основной причиной этого отдельного потока является необходимость выполнения всех действий в API (TTS от Loquendo) из одного потока.
Это означает, что мне нужно постоянно создавать экземпляр объекта TTS и вызывать методы для этого объекта из одного и того же потока.

Ответы [ 4 ]

14 голосов
/ 11 апреля 2010

Я нашел ответ!

Чтобы поддерживать поток, требуется дополнительный фрагмент кода:

- (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];
}


И следующая строка:

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

Должен быть заменен следующим:

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

РЕДАКТИРОВАТЬ : Как было предложено немногими здесь, я добавил несколько строк кода (NSAutoreleasePool, метод addPort и логическое значение isAlive).

13 голосов
/ 13 июля 2010

Это то, что работает для меня. Основной цикл взят из документации 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];
}

Надеюсь, это поможет

4 голосов
/ 08 мая 2010

Ну, я полагаю, у меня есть лучшее решение

- (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];
}

Что я здесь делаю?
1) Добавление здесь фиктивного порта в качестве источника предотвратит немедленный выход из метода runMode:beforeDate:.
2) Метод runMode:beforeDate: блокирует поток, пока в runLoop что-то не появится.

1 голос
/ 06 апреля 2010

Вы создали поток, но он не запущен. Это должно бежать, чтобы выполнить что-то.

Вместо этого вы также можете использовать "executeSelectorInBackground". Он будет ставить вызов в очередь до завершения инициализации.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...