Вопросы по темам и автозапуску

Насколько я понимаю, есть несколько способов отправки задач для выполнения в потоках. Самые распространенные из них:

1) performSelector: withObject: afterDelay:

2) performSelectorOnMainThread: withObject: waitUntilDone:

3) performSelectorInBackground: withObject:

4) [NSThread detachNewThreadSelector: toTarget: withObject:]

Мой первый вопрос: в чем разница между 1) и 2), помимо очевидных различий в параметрах? Действительно ли они оба работают в основном потоке (чей пул автозапуска был автоматически создан в main.m)? Я только что прочитал из чьей-то публикации в Stackoverflow, что метод 1) фактически работает в новом потоке, поэтому для его метода выбора должен быть создан пул автозапуска. Это правильно? Я много использовал 1), в основном чтобы воспользоваться параметром задержки, но я никогда не создавал для них пул автозапуска. Ничего катастрофического не произошло.

Далее, 3) и 4) оба выполняют задачи в отдельном потоке. Я слышал, что элементы пользовательского интерфейса никогда не должны выполняться в этих потоках, но я не понимаю, что именно является пользовательским интерфейсом. Я пытался написать код, чтобы в основном воспроизводить повторяющуюся анимацию загрузки, пока табличное представление запускается модально из контроллера навигации. Затем анимация останавливается в методе viewDidLoad контроллера tableview. Изначально я просто вставил код для запуска анимации над строками кода, которые запускают модальное представление. Что случилось, так это то, что анимацию так и не проиграли.

[[self loadingView] playAnimation];

SettingsViewController *menus = [[SettingsViewController alloc] initWithNibName:@"SettingsViewController" bundle:nil];

MyNavigationController *navController = [[MyNavigationController alloc] initWithRootViewController:menus];

[menus setParent:navController];
[navController setDelegate:self];
menus.mainViewController = self;

[self presentModalViewController:navController animated:YES];
[navController release];
[menus release];

Затем я попробовал следующее, и это сработало ...

[NSThread detachNewThreadSelector:@selector(settingsOpeningThread) toTarget:self withObject:nil];
[[self loadingView] playAnimation];



- (void) settingsOpeningThread {

NSAutoreleasePool *apool = [[NSAutoreleasePool alloc] init];

SettingsViewController *menus = [[SettingsViewController alloc]   initWithNibName:@"SettingsViewController" bundle:nil];

MyNavigationController *navController = [[MyNavigationController alloc] initWithRootViewController:menus];

[menus setParent:navController];
[navController setDelegate:self];
menus.mainViewController = self;

[self presentModalViewController:navController animated:YES];
[navController release];
[menus release];

[apool release];

}

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

Извините за досадно длинный пост. Любая помощь будет оценена по достоинству!


person kaka123    schedule 17.09.2011    source источник


Ответы (1)


(1) планирует задачу для текущего цикла выполнения. На очень высоком уровне приложение UIKit выглядит как

while(true) {
  update UI
  run all tasks that were scheduled last time through the loop
}

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

Обратите внимание, что performSelector:withObject:afterDelay не выполняет указанный код в отдельном потоке.

(2) делает что-то очень похожее, но вместо того, чтобы планировать что-то для текущего цикла выполнения, он планирует что-то для цикла выполнения в основном потоке. Это полезно, только если вызывается из отдельного потока, обычно потому, что вы хотите обновить пользовательский интерфейс из вторичного потока.

И да, ваш код слегка выделен жирным шрифтом. Я бы посоветовал сделать что-то вроде:

[[self loadingView] playAnimation];
[self performSelector:@selector(loadTable) withObject:nil afterDelay:0]

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

Однако это все равно не сработает, если для выполнения анимации требуется вмешательство основного потока. То есть, если код для загрузки таблицы останавливает основной поток, ваша анимация тоже может остановиться. На самом деле нет другого пути, кроме как выполнять долгосрочную задачу в отдельном потоке (который может использовать или не использовать performSelector:onMainThread:waitUntilDone для планирования обновлений пользовательского интерфейса в основном потоке).

Если вас не слишком заботит сама анимация, вы можете найти что-то вроде https://github.com/samvermette/SVProgressHUD полезно.

person edsko    schedule 10.10.2011