Обсуждение

Обсуждение

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

Чтобы оценить, насколько полезны потоки, проведем эксперимент. Предположим, у нас есть три цикла:

— (void) firstCounter{

NSUInteger counter = 0;

for (counter = 0;

counter < 1000;

counter++){

NSLog(@"First Counter = %lu", (unsigned long)counter);

}

}

— (void) secondCounter{

NSUInteger counter = 0;

for (counter = 0;

counter < 1000;

counter++){

NSLog(@"Second Counter = %lu", (unsigned long)counter);

}

}

— (void) thirdCounter{

NSUInteger counter = 0;

for (counter = 0;

counter < 1000;

counter++){

NSLog(@"Third Counter = %lu", (unsigned long)counter);

}

}

Очень просто, правда? Все циклы проходят от 0 до 1000, выводя на консоль номера счетчиков. Теперь предположим, что вы хотите, как обычно, запустить эти счетчики:

— (void) viewDidLoad{

[super viewDidLoad];

[self firstCounter];

[self secondCounter];

[self thirdCounter];

}

Этот код не обязательно должен находиться в методе viewDidLoad контроллера вида.

Теперь откройте окно консоли и запустите это приложение. Вы увидите, как сначала целиком выполнится первый счетчик, потом второй и, наконец, третий. Это означает, что данные циклы выполняются в одном и том же потоке. Каждый счетчик блокирует исполнение остального кода, относящегося к потоку, пока этот счетчик не завершит свой цикл.

А что, если бы мы захотели запустить все счетчики одновременно? Разумеется, для каждого из них нам пришлось бы создать отдельный поток. Но подождите! Мы ведь уже знаем, что прямо при загрузке приложение само создает для нас потоки. Кроме того, весь код, который мы уже успели создать для приложения, когда бы он ни был написан, исполняется в полученном потоке. Итак, мы уже создали по потоку для первого и второго счетчиков, а третий счетчик будет работать в главном потоке:

— (void) firstCounter{

@autoreleasepool {

NSUInteger counter = 0;

for (counter = 0;

counter < 1000;

counter++){

NSLog(@"First Counter = %lu", (unsigned long)counter);

}

}

}

— (void) secondCounter{

@autoreleasepool {

NSUInteger counter = 0;

for (counter = 0;

counter < 1000;

counter++){

NSLog(@"Second Counter = %lu", (unsigned long)counter);

}

}

}

— (void) thirdCounter{

NSUInteger counter = 0;

for (counter = 0;

counter < 1000;

counter++){

NSLog(@"Third Counter = %lu", (unsigned long)counter);

}

}

— (void)viewDidLoad {

[super viewDidLoad];

[NSThread detachNewThreadSelector:@selector(firstCounter)

toTarget: self

withObject: nil];

[NSThread detachNewThreadSelector:@selector(secondCounter)

toTarget: self

withObject: nil];

/* Этот код запускаем в главном потоке. */

[self thirdCounter];

}

У метода thirdCounter нет автоматически высвобождаемого пула, поскольку он не работает в новом открепленном потоке. Этот метод будет выполняться в главном потоке приложения, а главный поток располагает автоматически высвобождаемым пулом. Данный пул создается автоматически при написании любого приложения Cocoa Touch.

Ближе к концу кода мы видим вызовы селектора detachNewThreadSelector, предназначенные для запуска первого и второго счетчиков в отдельных потоках. Теперь, запустив приложение, вы увидите в окне консоли примерно следующий вывод:

Second Counter = 921

Third Counter = 301

Second Counter = 922

Second Counter = 923

Second Counter = 924

First Counter = 956

Second Counter = 925

First Counter = 957

Second Counter = 926

First Counter = 958

Third Counter = 302

Second Counter = 927

Third Counter = 303

Second Counter = 928

Иными словами, все три счетчика работают одновременно и их вывод перемежается случайным образом.

Каждый поток должен создавать автоматически высвобождаемый пул. Внутри такого пула содержатся ссылки на объекты, автоматически высвобождаемые до того, как будет высвобожден весь пул. Это очень важный механизм, действующий при управлении памятью с подсчетом ссылок в таких окружениях, как Cocoa Touch, то есть в средах, где объекты могут автоматически высвобождаться. Всякий раз при выделении экземпляра объекта количество ссылок на объект становится равным 1. Если пометить объекты как автоматически высвобождаемые, то количество ссылок на объект остается равным 1, но только до того момента, как высвободится тот пул, в котором создан объект. При высвобождении всего пула объект также получает сообщение release. Если на данный момент количество ссылок на объект так и осталось равным 1, объект высвобождается.

В каждом потоке необходимо создавать автоматически высвобождаемый пул, причем это должен быть самый первый объект, создаваемый в конкретном потоке. Если этого не сделать, то любой объект, создаваемый в потоке на протяжении его существования, будет вызывать утечку памяти. Чтобы лучше понять эту проблему, рассмотрим следующий код:

— (void) autoreleaseThread:(id)paramSender{

NSBundle *mainBundle = [NSBundle mainBundle];

NSString *filePath = [mainBundle pathForResource:@"AnImage"

ofType:@"png"];

UIImage *image = [UIImage imageWithContentsOfFile: filePath];

/* Делаем что-нибудь с изображением. */

NSLog(@"Image = %@", image);

}

— (void)viewDidLoad {

[super viewDidLoad];

[NSThread detachNewThreadSelector:@selector(autoreleaseThread:)

toTarget: self

withObject: self];

}

Если запустить этот код и одновременно следить за окном консоли, то можно увидеть примерно следующее сообщение:

*** __NSAutoreleaseNoPool(): Object 0x5b2c990 of

class NSCFString autoreleased with no pool in place — just leaking

*** __NSAutoreleaseNoPool(): Object 0x5b2ca30 of

class NSPathStore2 autoreleased with no pool in place — just leaking

*** __NSAutoreleaseNoPool(): Object 0x5b205c0 of

class NSPathStore2 autoreleased with no pool in place — just leaking

*** __NSAutoreleaseNoPool(): Object 0x5b2d650 of

class UIImage autoreleased with no pool in place — just leaking

Эти данные свидетельствуют о том, что созданный нами автоматически высвобождаемый экземпляр UIImage приводит к утечке памяти. Более того, утечку вызывают и экземпляр класса NSString под названием FilePath, а также другие объекты, которые в обычной ситуации спокойно высвободились бы. Дело в том, что при создании потока мы забыли первым делом выделить и инициализировать автоматически высвобождаемый пул — именно первым делом. Далее приведен правильный код. Можете сами его протестировать и убедиться, что никаких утечек не возникает:

— (void) autoreleaseThread:(id)paramSender{

@autoreleasepool {

NSBundle *mainBundle = [NSBundle mainBundle];

NSString *filePath = [mainBundle pathForResource:@"AnImage"

ofType:@"png"];

UIImage *image = [UIImage imageWithContentsOfFile: filePath];

/* Делаем что-то с изображением. */

NSLog(@"Image = %@", image);

}

}

— (void)viewDidLoad {

[super viewDidLoad];

[NSThread detachNewThreadSelector:@selector(autoreleaseThread:)

toTarget: self

withObject: self];

}

Данный текст является ознакомительным фрагментом.



Поделитесь на страничке

Следующая глава >

Похожие главы из других книг:

Обсуждение

Из книги автора

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


Обсуждение

Из книги автора

Обсуждение Фреймворк Assets Library — удобный посредник между разработчиком и библиотекой фотографий. Как будет указано в разделе 13.6, в iOS SDK вам предоставляются встроенные компоненты графического пользовательского интерфейса, которыми можно пользоваться для доступа к


Обсуждение

Из книги автора

Обсуждение Чтобы пользователь мог выбирать фотоснимки или видеоролики из своей библиотеки фотографий, необходимо установить свойство sourceType экземпляра UIImagePickerController в значение UIImagePickerControllerSourceTypePhotoLibrary и только потом открывать перед пользователем инструмент для выбора


Обсуждение

Из книги автора

Обсуждение Библиотека ресурсов подразделяется на группы. В каждой группе содержатся ресурсы, а каждый ресурс имеет свойства, например URL (универсальные локаторы ресурсов) и объекты представления.Все ресурсы всех типов можно получать из библиотеки ресурсов с помощью


Обсуждение

Из книги автора

Обсуждение Класс UIVideoEditorController, содержащийся в iOS SDK, позволяет программисту вывести на экран перед пользователем специальный интерфейс для редактирования. Все, что требуется сделать, — предоставить URL видеоролика, который предполагается отредактировать, а потом


Обсуждение

Из книги автора

Обсуждение В зависимости от того, на работу в какой версии iOS рассчитано ваше приложение, его можно запускать и выполнять на различных устройствах, где установлены разные версии iOS. Например, вы можете разрабатывать приложение в последней версии iOS SDK, но в качестве целевой


Обсуждение

Из книги автора

Обсуждение Когда приложение переходит в фоновый режим, работа его основного потока приостанавливается. Потоки, которые вы создаете в своем приложении с помощью метода класса detachNewThreadSelector: toTarget: withObject:, относящегося к классу NSThread, также приостанавливаются. Если вы


Обсуждение

Из книги автора

Обсуждение Многие приложения, ежедневно поступающие на рынок App Store, обладают возможностями соединения с теми или иными серверами. Некоторые выбирают с сервера данные для обновления, другие отсылают информацию на сервер и т. д. В течение долгого времени в iOS существовал


Обсуждение

Из книги автора

Обсуждение В iOS приложение может запросить продолжить воспроизведение своих аудиофайлов, даже если оно само переходит в фоновый режим. В этом разделе мы воспользуемся плеером AVAudioPlayer, который прост и удобен в обращении. Наша задача — запустить аудиоплеер и воспроизвести


Обсуждение

Из книги автора

Обсуждение Когда приложение работает в приоритетном режиме, можно получать от экземпляра CLLocationManager делегатные сообщения, информирующие вас о том, что iOS обнаружила перемещение устройства на новое место. Однако если приложение переходит в фоновый режим и становится


Обсуждение

Из книги автора

Обсуждение Допустим, пустое приложение iOS (то есть приложение всего с одним окном, для которого еще не написан код) впервые запускается на устройстве с iOS, поддерживающем работу в многозадачном режиме. Оно запускается именно впервые, а не возвращается из фонового режима в


Обсуждение

Из книги автора

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


Обсуждение

Из книги автора

Обсуждение Пока ваше приложение работает в фоновом режиме, может произойти многое! Например, пользователь может вдруг изменить локализацию устройства с iOS на странице Settings (Настройки) и задать, к примеру, испанский язык вместо английского. Приложения могут


Обсуждение

Из книги автора

Обсуждение В приложениях, написанных для iOS, файл пакета настроек может быть предоставлен пользователю для внесения собственных настроек. Эти настройки будут доступны пользователю в приложении (Settings) на устройстве. Чтобы лучше понять, как работает этот механизм,


Обсуждение

Из книги автора

Обсуждение Пуш-уведомления похожи на локальные уведомления тем, что позволяют сообщать пользователю определенную информацию, даже если ваше приложение неактивно при поступлении уведомления. В то время как локальные уведомления назначаются самим приложением,