Состояние UIButton и ход селектора

У меня возникли некоторые проблемы с отображением хода выполнения более длительного действия основного потока (должно быть в основном потоке).

Действие вызывается нажатием кнопки.

-(void)getCSVExport:(id)sender{
    ...
    NSString *filePath = [path stringByAppendingPathComponent:fileName];
    NSData *csvData = [NSData dataWithContentsOfFile:filePath];
    if (nil == csvData) {
       _progressView.hidden = NO;
       [self.view bringSubviewToFront:_progressView];
       _progressView.progress = 0;
       csvData = [self generateCSVExportForMonth:_monthToExportInt];
       [csvData writeToFile:filePath atomically:YES];
       _progressView.hidden = YES;
    }
    ...
}

внутри функции generateCSVExportForMonth: я обновляю прогресс с помощью _progressView.progress = newValue.

у меня сейчас 2 проблемы:

1) при нажатии кнопки вызова getCSVExport: кнопка остается подсвеченной до завершения вызова. 2) progressView никогда не отображается, не говоря уже о самом обновлении.

информация: вызов занимает от 0,5 до 2 секунд в зависимости от устройства.

есть идеи, где я ошибся?

// РЕДАКТИРОВАТЬ: новая версия с backgroundThread:

[self.view bringSubviewToFront:_progressView];
_progressView.progress = 0;
[self performSelector:@selector(assignCSVData:) onThread:[NSThread new] withObject:csvData waitUntilDone:YES];
_progressView.hidden = YES;

и дорогой по времени звонок:

-(void)assignCSVData:(NSData*)data{
    data = [self generateCSVExportForMonth:_monthToExportInt];
 }

это приводит к взаимоблокировке при вызове performSelector.


person Sebastian Flückiger    schedule 03.04.2013    source источник


Ответы (1)


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

Я не понимаю, почему что-то вроде синтаксического анализа CSV-файла абсолютно должно выполняться в основном потоке. Вам придется делать это в фоновом режиме (или жить с плохим пользовательским интерфейсом зависшего графического интерфейса).

У вас есть несколько вариантов, как на самом деле реализовать что-то подобное.

  • Используйте performSelectorInBackground:withObject: от NSObject

    Поместите код синтаксического анализа в отдельный метод и запустите его в фоновом потоке, используя:

    [self performSelectorInBackground:@selector(parseMethod) withObject:csvData];
    

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

    [self performSelectorOnMainThread:@selector(parsingDone:) withObject:result waitUntilDone:NO];
    
  • Используйте Grand Central Dispatch (GCD) для запуска некоторого кода в фоновом режиме с использованием блочного синтаксиса. Также довольно просто, но немного сложнее синтаксис и семантика, если вы привыкли к Objective-C и Cocoa API.

  • Используйте NSOperation и NSOperationQueue. Вероятно, немного накладных расходов для вашей цели. Хотя вы также можете легко добавить новую операцию в очередь, вызвав addOperationWithBlock без подкласса NSOperation.

person DrummerB    schedule 03.04.2013
comment
моя проблема в том, что метод, генерирующий CSV-файл, обращается к coredata. У меня были взаимоблокировки из-за этого в прошлом, и я хочу этого избежать. разрешить пользователю ждать максимум 2 секунды один раз за время существования приложения — отчет никогда не будет воссоздан, а сохранен и использован повторно — был мой план для обеспечения стабильности. другой способ заключается в том, что мне нужно убедиться, что приложение остается в том же месте (контроллер представления) до тех пор, пока отчет не будет сгенерирован, потому что он будет использоваться только там: / если есть умный способ сделать это, я был бы рад, поскольку хорошо :) - person Sebastian Flückiger; 03.04.2013
comment
Он постоянно полагается на объекты Cora Data? Разве вы не можете просто получить те объекты, которые вам нужны, и передать их анализатору в фоновом режиме? - person DrummerB; 03.04.2013
comment
отчет занимает ровно 5+(numberOfDaysOfMonth) выборок, постоянно распределенных по выполнению функции. Выборки сильно различаются между выборками объектов и свойств. получение всего сразу привело бы к большой потере производительности из-за утомительной сортировки результатов. - person Sebastian Flückiger; 03.04.2013
comment
предполагая, что я мог бы сделать его потокобезопасным. как мне вызвать csvData = [self generateCSVExportForMonth:_monthToExportInt]; в другом потоке? это время дорогого звонка. - person Sebastian Flückiger; 03.04.2013
comment
Вы должны быть несколько осторожны с Core Data и фоновыми потоками. Никогда не передавать фактический NSManagedObject другому потоку (только идентификатор объекта). А еще лучше создать отдельный NSManagedObjectContext в фоновом потоке. Но если это действительно занимает не более 2 секунд, вы, вероятно, можете сделать это в основном потоке и обновить индикатор выполнения с помощью некоторого трюк с запуском цикла... - person omz; 03.04.2013
comment
я добавил немного больше кода, пытаясь выполнить затратный по времени вызов в фоновом режиме - к сожалению, это не работает так просто, как я надеялся:/ - person Sebastian Flückiger; 03.04.2013
comment
@omz спасибо за эту ссылку, она выглядит многообещающе. во всех моих тестах я никогда не превышал 0,9 секунды, но предполагая, что какой-то случайный пользователь будет иметь двойную сумму, чем мои тестовые данные (которые были чрезвычайно большими), я не верю, что 2 секунды возможны;) спасибо за подсказку! - person Sebastian Flückiger; 03.04.2013
comment
@DrummerB спасибо за обновленный ответ :) я думаю, что отсутствующий обратный вызов был моей проблемой. Спасибо за уделенное время. - person Sebastian Flückiger; 03.04.2013