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

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

1) executeSelector: withObject: afterDelay:

2) executeSelectorOnMainThread: withObject: waitUntilDone:

3) executeSelectorInBackground: withObject:

4) [NSThread detachNewThreadSelector: toTarget: withObject:]

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

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

[[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 не будет полностью запущено. Но стоит ли запускать модальные представления, подобные этому, как "пользовательский интерфейс" и их следует избегать? Также я получаю некоторые странные ошибки утечки памяти в Инструментах каждый раз, когда запускается модальное представление. Но это из одной из "системных библиотек", о которой мне сказали, что ее очень трудно отлаживать. Что может быть не так?

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

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, Это означает, что когда появится runloop, ваш пользовательский интерфейс будет обновлен, анимация начнет воспроизводиться, затем loadTable метод вызывается и выполняет свою работу.

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

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

Другие вопросы по тегам