Как обновить элементы управления пользовательского интерфейса в приложении какао из фонового потока - PullRequest
2 голосов
/ 29 мая 2010

следующие .m код:

#import "ThreadLabAppDelegate.h"
@interface ThreadLabAppDelegate()

- (void)processStart;
- (void)processCompleted;

@end


@implementation ThreadLabAppDelegate

@synthesize isProcessStarted;

- (void)awakeFromNib {
    //Set levelindicator's maximum value
    [levelIndicator setMaxValue:1000];
}

- (void)dealloc {
    //Never called while debugging ????
    [super dealloc];
}

- (IBAction)startProcess:(id)sender {
    //Set process flag to true
    self.isProcessStarted=YES;

    //Start Animation
    [spinIndicator startAnimation:nil];

    //perform selector in background thread
    [self performSelectorInBackground:@selector(processStart) withObject:nil];
}

- (IBAction)stopProcess:(id)sender {
    //Stop Animation
    [spinIndicator stopAnimation:nil];

    //set process flag to false
    self.isProcessStarted=NO;
}

- (void)processStart {
    int counter = 0;
    while (counter != 1000) {
        NSLog(@"Counter : %d",counter);

        //Sleep background thread to reduce CPU usage
        [NSThread sleepForTimeInterval:0.01];

        //set the level indicator value to showing progress
        [levelIndicator setIntValue:counter];

        //increment counter
        counter++;
    }

    //Notify main thread for process completed
    [self performSelectorOnMainThread:@selector(processCompleted) withObject:nil waitUntilDone:NO];

}

- (void)processCompleted {
    //Stop Animation
    [spinIndicator stopAnimation:nil];

    //set process flag to false
    self.isProcessStarted=NO;
}
@end 

Мне нужно очистить следующие вещи в соответствии с приведенным выше кодом.

  1. Как прервать / отменить processStart в то время как цикл из управления пользовательского интерфейса?
  2. Мне также нужно показать значение счетчика в основном пользовательском интерфейсе, что я предполагаю сделать с executeSelectorOnMainThread и передачей аргумента.Просто хочу знать, есть ли другой способ сделать это?
  3. Когда мое приложение запустилось, оно отображает 1 поток в Activity Monitor, но когда я запустил processStart () в фоновом потоке, он создает два новых потока, которыеделает все 3 потока до тех пор, пока цикл не будет завершен. После завершения цикла я вижу 2 потока.Итак, я понимаю, что 2 потока были созданы, когда я вызвал executeSelectorInBackground, но как насчет третьего потока, откуда он был создан?
  4. Что если число потоков увеличивается при каждом вызове селектора. Как управлять этимили моя реализация плоха для такого рода требований?

Спасибо

1 Ответ

6 голосов
/ 29 мая 2010

как обновить элементы управления пользовательского интерфейса в приложении какао из фонового потока

Простой: Не.

Как прервать/ отменить processStart в то время как цикл из управления пользовательского интерфейса?

За пределами processStart, установите переменную флага.Внутри processStart проверьте этот флаг и выйдите из цикла, если он установлен.

Не пытайтесь «убить» поток из другого потока.Это всегда плохая идея. Сообщите потоку, что пришло время остановиться, установив флаг, и попросите поток проверить этот флаг и остановиться в соответствующее время.

Мне также нужно показатьзначение счетчика в основном пользовательском интерфейсе, который я предполагаю делать с executeSelectorOnMainThread и передачей аргумента.Просто хочу знать, есть ли другой способ сделать это?

Да.

Когда мое приложение запускалось, в Activity Monitor показывается 1 поток, но когда я запускалprocessStart () в фоновом потоке создает два новых потока, которые составляют 3 потока до тех пор, пока цикл не будет завершен. После завершения цикла я вижу 2 потока.Итак, я понимаю, что 2 потока были созданы, когда я вызвал executeSelectorInBackground, но как насчет третьего потока, откуда он был создан?

Профилируйте ваше приложение с помощью инструментов или Shark и смотрите.Вероятно, это поток сердцебиения для индикатора прогресса.

Что, если число потоков увеличивается при каждом вызове селектора. Как контролировать это или моя реализация плоха для таких требований?

Каждое сообщение performSelectorInBackground:withObject: запускает поток.Если количество потоков не уменьшается, это потому, что ваш метод потока не завершился.Если число потоков слишком велико, это (вероятно) потому, что вы запустили слишком много потоков.


Существует гораздо лучший способ сделать это.

Во-первых, общее правило вКакао никогда не спит .Думайте об этом как о специальном какао с ультра-кофеином.Для всего, о чем вы могли бы спать в другой среде, почти всегда есть лучший, обычно более простой способ в Какао.

Имея это в виду, посмотрите на processStart.Все, что он делает, это делает что-то каждую секунду.Как лучше всего это сделать?

Какао имеет класс для этой конкретной цели: NSTimer.Создайте таймер, который отправляет вам сообщение с желаемым интервалом, и отвечайте на это сообщение, обновляя индикатор выполнения, то есть ваш метод обратного вызова таймера должен быть просто телом цикла из processStart, без цикла.

Кстати, 100 обновлений в секунду - это перебор.Прежде всего, пользователю все равно, что вы продвинулись на 1/5 от значения пикселя с момента последнего обновления панели.Во-вторых, экран обновляется только около 60 раз в секунду , поэтому обновление всего видимого быстрее, чем это бессмысленно.

- (void)dealloc {
    //Never called while debugging ????
    [super dealloc];
}

Предполагается, что вы добавили делегата приложенияиз-за этого перо MainMenu объект приложения владеет им, но он этого не знает, потому что знает только о делегате приложения в качестве своего делегата, который не принадлежит.(И даже если бы это были отношения владения, это были бы только два владельца, из которых приложение выпустило бы одно, что не помогло бы.)

Однако время жизни делегата приложения на самом деле неиметь значение.Его назначение в качестве делегата приложения означает, что оно должно длиться примерно столько же, сколько и приложение, но когда приложение уходит, процесс завершается, что означает, что делегат также будет освобожден, как часть восстановленияпространство памяти процесса.Вот почему dealloc не вызывается - все пространство процесса исчезает сразу, вместо того, чтобы объекты освобождались по одному за раз.

Так что, в принципе, да, делегат приложения не был явно очищенэто немного хитроумно.На практике не помещайте никакие временные файлы очистки в dealloc (используйте вместо applicationWillTerminate:), и все будет в порядке.

Я делаючтобы обойти эту проблему, поместите всю мою реальную работу в один или несколько других объектов, которыми владеет делегат приложения. Делегат приложения создает эти другие контроллеры в applicationWillFinishLaunching: и выпускает их в applicationWillTerminate:, поэтому эти объекты действительно получают dealloc сообщения. Проблема решена.

...