Как отправить селектор или блоки в NSRunLoop для выполнения?

Мне нужно гарантировать, что тот же поток выполняет различные действия в произвольные моменты времени. Сначала поток должен инициализировать библиотеку, затем я хочу, чтобы поток спал до тех пор, пока работа не будет выполнена, и при вводе пользователя мне нужно будет передать селектор или блоки для выполнения.

Как настроить NSRunLoop для сна после инициализации? После чего, как я могу сигнализировать прогон цикла, чтобы проснуться и что-то сделать?

Я пробовал читать руководство по программированию Threading для iOS, но я бы хотел не создавать классы в качестве пользовательских классов ввода и использовать что-то более легкое, например performSelector:onThread:

Могу ли я установить таймер на огонь навсегда, так что цикл выполнения не заканчивается?

Здесь по существу то, что я хочу в псевдокоде:

// Initialization Code...

do {
    sleepUntilSignaled();
    doWorkSentToThisThread();
while (!done);

Где я отправляю работу, чтобы сделать это как сообщение performSelector:onThread:. Было бы даже лучше, если бы я мог отправить цикл цикла блоку вроде: ^{[someObj message]; [otherObj otherMsg];}, но я был бы доволен performSelector, так как я уверен, что это возможно без особого дополнительного кодирования.

Спасибо!

Ответы

Ответ 1

У вас есть все необходимые части в вашем вопросе. Вы начинаете свой поток и запускаете его runloop. Если вам нужен поток, чтобы сделать что-то, вы можете использовать performSelector:onThread: в основном потоке, чтобы сделать это.

Есть одна вещь с runloop, которую вы должны знать: она не будет работать, если у нее нет источника ввода или таймера, подключенного к нему. Просто присоедините таймер к циклу запуска, который запускает некоторое время в отдаленном будущем, и вы все установите.

// Initialization code here

[NSTimer scheduledTimerWithTimeInterval: FLT_MAX
                                 target: self selector: @selector(doNothing:)
                               userInfo: nil repeats:YES];

NSRunLoop *rl = [NSRunLoop currentRunLoop];
do {
    [rl runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
} while (!done);

Используя performSelector:onThread:withObject:, вы также можете передать свой блок в фоновый поток. Все, что вам нужно сделать, это написать метод где-нибудь, который принимает блок как параметр и запускает его:

@interface NSThread (sendBlockToBackground)
- (void) performBlock: (void (^)())block;
@end

@implementation NSThread (sendBlockToBackground)
- (void) performBlock: (void (^)())block;
{
    [self performSelector: @selector(runBlock:) 
                 onThread: self withObject: block waitUntilDone: NO];
}

- (void) runBlock: (void (^)())block;
{
    block();
}
@end

Но, возможно, вы должны использовать очередь отправки вместо всего этого. Для этого требуется меньше кода и, вероятно, также имеет меньше накладных расходов:

dispatch_queue_t myQueue = dispatch_queue_create( "net.example.product.queue", NULL );
dispatch_async( myQueue, ^{
    // Initialization code here
} );

// Submit block:
dispatch_async( myQueue, ^{
    [someObject someMethod: someParameter];
} );

Очередь отправки, созданная с помощью dispatch_queue_create, является последовательной очередью - все отправленные на нее блоки будут выполняться в том же порядке, в каком они были получены, один за другим.

Ответ 2

Рассмотрим использование NSConditionLock. Он предназначен для таких задач. Представьте, что у вас есть очередь с данными. Первый поток добавляет данные в очередь, второй поток ждет данных и обрабатывает их.

id condLock = [[NSConditionLock alloc] initWithCondition:NO_DATA];

//First thread
while(true)
{
    [condLock lock];
    /* Add data to the queue. */
    [condLock unlockWithCondition:HAS_DATA];
}

//Second thread
while (true)
{
    [condLock lockWhenCondition:HAS_DATA];
    /* Remove data from the queue. */
    [condLock unlockWithCondition:(isEmpty ? NO_DATA : HAS_DATA)];

    // Process the data locally.
}

Ответ 3

Я думаю, вы можете использовать NSInvocationOperation с NSOperationQueue.