2015-09-24 3 views
3

Я вызываю метод heartBeats за 10 мс в конкретном потоке (не в основном потоке), как вызвать другой метод в любое время в этом же потоке?Как запустить методы в одном конкретном потоке (не основной поток)

I подкласс NSThread как этот

@implementation MyThread 
{ 
    NSTimeInterval _lastTimeInterval; 
} 

- (void)main 
{ 

    while (true) { 

     NSTimeInterval timeInterval = [[NSDate date] timeIntervalSince1970]*1000; 

     if (timeInterval - _lastTimeInterval > 10) 
     { 
      [self heartBeats]; 

      _lastTimeInterval = timeInterval; 
     } 

    } 

} 

- (void)heartBeats 
{ 
    NSLog(@"heart beats thread: %@", [NSThread currentThread].description); 
} 

@end 

и запустить его, как этот

@implementation ViewController 

- (void)viewDidLoad { 
    [super viewDidLoad]; 

    NSLog(@"main thread: %@", [NSThread currentThread].description); 

    MyThread *myThread = [[MyThread alloc]init]; 
    [myThread start]; 
} 


- (void)someMethod 
{ 
    // do somthing 
} 

- (void)didReceiveMemoryWarning { 
    [super didReceiveMemoryWarning]; 
} 

@end 

Теперь, вот вопрос, как запустить - (void)someMethod в myThread?

+0

занят опрос, как это ужасно код в любом месте, но особенно на мобильном устройстве – Paulw11

+0

@ Paulw11 да, но это не может быть и речи, фактически, «heartBeats» - это API, который я должен назвать так, чтобы сохранить это длинное соединение. : ( –

+0

Замена кода 'if' на' [NSThread sleepForTimeInterval: 0,007]; 'снижение потребления процессора с 98% до 2% и увеличение интервалов между 7,6 мс и 9,8 м. Использование' [NSThread sleepForTimeInterval: 0,0002]; 'с ваш код if дал интервалы ближе к 10.3 мс и использовал 9% процессор, но короткий ответ - вам придется использовать какую-то синхронизацию и структуру данных с общей памятью, чтобы ваш цикл определял, что он должен выполнить какой-либо другой метод. t просто отправьте метод в потоке так, как вы можете, с помощью очереди GCD – Paulw11

ответ

4

Способ вашего подкласса NSThread - это все, что работает на этом потоке. Вы не можете прервать его, чтобы запустить другой код без сотрудничества main.

Что вы действительно должны сделать, это выбросить весь цикл и заменить его на NSRunLoop и NSTimer.

  • NSRunLoop держит нить в живых до тех пор, пока есть что-то, что нужно будет делать, но и спит нить до не нужно делать что-то.
  • NSTimer делает что-то на повторяющемся интервале, пока он запланирован в цикле запуска.

Вам нужна ваша нить, чтобы сделать две вещи:

  • отправить MyThread объект на heartBeats сообщение (вы делаете это)
  • отправить контроллер представления на someMethod сообщение (это то, что вы)

Для последнего вам понадобится еще одна вещь: источник цикла запуска.

Итак, ясно из вашего метода main и заменить его следующим:

  1. Получить текущий NSRunLoop и хранить его в локальной переменной.
  2. Создайте NSTimer с 10-секундным интервалом, целью которого является self, а селектор - heartBeats. (Немного чистая версия: используйте другой метод, который принимает NSTimer *, но игнорирует его, поэтому ваш таймер вызывает этот метод, и этот метод вызывает heartBeats. Правильный способ установки таймера - дать ему метод, который ожидается называть с таймером, но на практике также дает метод, который не принимает никаких аргументов.)
  3. Если вы не создали таймер, используя scheduledTimerWith…, добавьте его в цикл выполнения. (Методы scheduledTimerWith… сделают это для вас.)
  4. Создайте источник цикла запуска и добавьте его в цикл выполнения.
  5. Звонок [myRunLoop run].

Шаг 4 медведей, объясняющие:

Основной фонд (но не фонд, я не знаю, почему) есть то, что называется «источники пробег цикла», которые являются пользовательские объекты, которые могут быть добавлены в цикл выполнения и будет вызывать что-то однажды сигнализированное.

Похоже, что вы хотите, чтобы вызвать ваш метод контроллера точки зрения!

Во-первых, в контроллере просмотра измените значение myThread на локальную переменную в viewDidLoad на переменную экземпляра. Переименуйте его _myThread, чтобы сделать это понятным.

Следующая, добавить delegate собственности на MyThread. Это должно быть weak и иметь тип id <MyThreadDelegate>. Вам также необходимо определить протокол MyThreadDelegate, который должен иметь один метод, не принимающий никаких аргументов и не возвращающий ничего (void).

Теперь вы можете установить _myThread.delegate = self с контроллера вида и реализовать в контроллере представления метод, который вы указали в протоколе. Контроллер представления теперь является делегатом его MyThread.

В -[MyThread main], создать версию-0 CFRunLoopSource. Функция «Создать» принимает структуру «контекста», содержащую, среди прочего, версию (0), указатель «info» (задает это значение self, то есть MyThread) и обратный вызов Perform (функция, которая будет вызываться с указателем info в качестве единственного аргумента).

В вашем выполнения обратного вызова, вам нужно сделать что-то вроде этого:

MyThread *self = (__bridge MyThread *)info; 
[self fireDelegateMessage]; 

В MyThread, реализовать этот fireDelegateMessage метод. Там, отправьте сообщение self.delegate, которое вы указали в своем протоколе.

Затем добавьте общедоступный метод в MyThread (т. Е. Объявите его в MyThread.h, а также реализуйте его в MyThread.m), назвав что-то вроде «requestDelegateMessage». В этом методе позвоните по номеру CFRunLoopSourceSignal. (Документация для выполнения этой функции предполагает, что вы также должны вызвать CFRunLoopWakeUp. П потока CFRunLoop Попробуй без первого.)

Наконец, когда контроллер представления хочет someMethod называться по этой теме, вызовите [_myThread requestDelegateMessage].

Итак:

  1. контроллер вид вызывает requestDelegateMessage
  2. requestDelegateMessage сигнализирует источник цикла выполнения (и будит цикл выполнения, если это необходимо)
  3. источник цикла выполнения вызывает выполнение обратного вызова в потоке MyThread
  4. выполняют обратные вызовы fireDelegateMessage по теме MyThread
  5. fireDelegateMessage называет th внедрения электронного контроллера представления о методе делегата на нити в MyThread в
  6. контроллер вид вызывает someMethod на нити в MyThread в
+0

Спасибо за отличный ответ, он работает! Но я замечаю это, если я дам достаточно большой временной интервал таймеру, 'someMethod' задерживается. –

+0

@GaojinHsu: Это то, что делают таймеры, и почему почти всегда лучше делать что-то по необходимости с помощью метода делегата или отправленного блока, а не периодически с помощью таймера. –

+0

@GaojinHsu: Кроме того, я неправильно читаю ту часть, где вы сказали 10 мс, а не 10 секунд. 10 мс * действительно, очень часто! * Это буквально быстрее, чем обновления на экране (60 Гц = 1/60 секунды = 16 + 2/3 мс) и почти наверняка быстрее, чем это делает ваш таймер, выполнение работы * позволит таймеру обновляться так быстро, как вы этого хотели. Таймер, вероятно, не самый лучший способ сделать то, что вы делаете, в первую очередь, и если это так, это не должно быть так часто. –

Смежные вопросы