Метод performSelectorInBackground: withObject: создает в фоновом режиме новый поток. Ситуация эквивалентна созданию нового потока для селекторов. Самое важное, что в данном случае нужно учитывать: поскольку этот метод создает поток для конкретного селектора, у селектора должен быть автоматически высвобождаемый пул, как и у любого другого потока, который действует в среде, управляемой с применением подсчета ссылок.

<p>7.17. Выход из потоков и таймеров</p><p>Постановка задачи</p>

Требуется остановить поток или таймер либо не допустить его повторного запуска.

<p>Решение</p>

При работе с таймерами пользуйтесь методом экземпляра invalidate, относящимся к классу NSTimer. При работе с потоками используйте метод cancel. Старайтесь не применять метод exit при работе с потоками, так как он не позволяет потоку произвести после себя очистку, что в итоге приводит к утечке ресурсов из вашего приложения.

NSThread *thread = /* Здесь получаем ссылку на ваш поток. */;

[thread cancel];

NSTimer *timer = /* Здесь получаем ссылку на ваш таймер. */;

[timer invalidate];

<p>Обсуждение</p>

Выйти из таймера не составляет труда — можно просто вызвать метод экземпляра invalidate, относящийся к таймеру. После вызова этого метода таймер больше не будет инициировать никаких событий в своем целевом объекте.

А вот выходить из потоков немного сложнее. Когда поток находится в спящем режиме и вызывается его метод cancel, рабочий цикл этого потока выполнит свою задачу, а только потом осуществит выход. Рассмотрим это:

— (void) threadEntryPoint{

@autoreleasepool {

NSLog(@"Thread Entry Point");

while ([[NSThread currentThread] isCancelled] == NO){

[NSThread sleepForTimeInterval:4];

NSLog(@"Thread Loop");

}

NSLog(@"Thread Finished");

}

}

— (void) stopThread{

NSLog(@"Cancelling the Thread");

[self.myThread cancel];

NSLog(@"Releasing the thread");

self.myThread = nil;

}

— (BOOL) application:(UIApplication *)application

didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{

self.myThread = [[NSThread alloc]

initWithTarget: self

selector:@selector(threadEntryPoint)

object: nil];

[self performSelector:@selector(stopThread)

withObject: nil

afterDelay:3.0f];

[self.myThread start];

self.window = [[UIWindow alloc] initWithFrame:

[[UIScreen mainScreen] bounds]];

self.window.backgroundColor = [UIColor whiteColor];

[self.window makeKeyAndVisible];

return YES;

}

Данный код создает экземпляр класса NSThread и немедленно запускает поток. Поток в каждом цикле проводит 4 секунды в спящем режиме, а потом переходит к выполнению своей задачи. Тем не менее, прежде чем поток будет запущен, мы вызываем метод stopThread, относящийся к (написанному нами) контроллеру вида; это делается с трехсекундной задержкой. Данный метод вызывает метод cancel, относящийся к потоку, пытаясь заставить поток выйти из своего цикла. Теперь запустим приложение и посмотрим, что выводится в окне консоли:

Thread Entry Point

Cancelling the Thread

Releasing the thread

Thread Loop

Thread Finished

Итак, ясно видно, что перед выходом поток завершил текущий цикл, хотя запрос о выходе и был дан в середине цикла. Это очень распространенная ловушка. Чтобы избежать ее, нужно сначала проверять, не отменен ли поток, и лишь потом переходить к выполнению какой-либо задачи, для которой свойственны внешние побочные эффекты в цикле потока. Мы можем переписать код следующим образом. При этом операция с внешним эффектом (записыванием в регистрационный журнал) сначала проверяет, не отменен ли поток:

— (void) threadEntryPoint{

@autoreleasepool {

NSLog(@"Thread Entry Point");

while ([[NSThread currentThread] isCancelled] == NO){

[NSThread sleepForTimeInterval:4];

if ([[NSThread currentThread] isCancelled] == NO){

NSLog(@"Thread Loop");

}

}

Перейти на страницу:

Все книги серии Бестселлеры O'Reilly

Похожие книги