Многопоточное программирование в среде Cocoa, Mac OS, iOS

Управление потоками

Каждый процесс (приложение) в Mac OS X или iOS состоит из одного или нескольких потоеов, каждый из которых представляет собой отдельный путь выполнения с помощью кода приложения. Каждое приложение начинается с одного потока, который порождает main функция приложения. Приложения могут порождать дополнительные потоки, каждый из которых выполняет код конкретной функции.

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

В этом разделе дается обзор потоковых технологий, доступных в Mac OS X и iOS, а также примеры того, как использовать эти технологии в своих приложениях.

Затраты потоков

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

Таблица 2-1 количественно представляет приблизительные расходы, связанные с созданием новых пользовательских потоков в вашем приложении. Некоторые из этих затрат являются настраиваемыми, такие как объем стека, выделяемых на вторичных потоках. Затраты времени на создание потока в грубом приближении и должны использоваться только для относительного сравнения друг с другом. Время создания потока может сильно варьироваться в зависимости от загрузки процессора, скорости компьютера, объемом доступной системой и программой памяти.

Таблица 2-1 расходы на создание потока

ПозицияОриентировочная стоимостьПримечания
Структуры данных ядраОколо 1 KBЭта память используется для хранения данных потока структур и атрибутов, большая часть которых выделяется как wired память и, следовательно, не может быть выгружена на диск.
Пространство стека

512 KB (для вторичных потоков)

8 MB (Mac OS X основной поток)

1 MB (iOS основной поток)

Минимально допустимый размер стека для вторичных потоков составляет 16 Кб, и размер стека должен быть кратен 4 КБ. Пространство для этой памяти откладывается в пространстве процесса в потоке во время создания, но сами страницы, связанные с этой памятью не создаются, пока в ней нет необходимости.
Время создания Около 90 микросекундЭто значение отражает время между начальным вызовом создать поток и временем, за которое точка входа потока начала выполняться. Цифры были определены путем анализа средней величины, образующейся при создании потоков на основе Intel iMac с 2 ГГц процессором Core Duo и 1 ГБ оперативной памяти под управлением Mac OS X 10.5.

 

Примечание: Из-за своей поддержки основного ядра, операции объектов часто создают потоки быстрее. Вместо того чтобы создавать потоки с нуля каждый раз, они используют пулы потоков, уже сохраненных в ядре, чтобы сэкономить на распределении временя. Для получения дополнительной информации об использовании операции объектов см. в "Параллелизм".

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

Создание потоков

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

Использование NSThread

Есть два способа создания потока c использованием класса NSThread:

  • Использование метода класса detachNewThreadSelector:toTarget:withObject:, чтобы породить новый поток.
  • Создание нового объекта NSThread и вызов его метода start. (Поддерживается только в iOS и Mac OS X 10.5 и выше.)

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

Поскольку detachNewThreadSelector:toTarget:withObject: метод поддерживается всеми версиями Mac OS X, он часто встречается в существующих Cocoa приложениях, использующих потоки. Чтобы отключить новый поток, просто укажите имя метода (указывается в качестве селектора), который вы хотите использовать в качестве исходной точки потока, объект, который определяет, этот метод, и любые данные, которые вы хотите передать в поток при запуске . В следующем примере показаны основы вызова этого метода, для порождения потока, используя специальный метод текущего объекта.

[NSThread detachNewThreadSelector:@selector(myThreadMainMethod:) toTarget:self withObject:nil];

До Mac OS X v10.5, вы использовали NSThread класс в первую очередь для порождения потока. Хотя вы могли бы получить NSThread объект и получить доступ к атрибутам потока, вы могли сделать это только из потока и только после того, как он был запущен. В Mac OS X 10.5, была добавлена ​​поддержка создания объектов без NSThread сразу порождая соответствующий новый поток. (Эта поддержка также доступна в iOS). Данная поддержка позволила получать и устанавливать различные атрибуты потока до его старта. Это также позволило использовать объект потока в ссылках на поток, который будет запущен позже.

Простой способ инициализации NSThread объекта в Mac OS X 10.5 и более поздних является использование метода initWithTarget:selector:object:. Этот метод имеет точно такую ​​же информацию, как метод detachNewThreadSelector:toTarget:withObject: и использует его для инициализации нового экземпляра NSThread. Однако он не запускает поток. Чтобы запустить поток, вы вызываете метод start объекта потока в явном виде, как показано в следующем примере:

NSThread* myThread = [[NSThread alloc] initWithTarget:self
       selector:@selector(myThreadMainMethod:) object:nil];

[myThread start];  // На самом деле создает поток

Примечание: Вместо использования initWithTarget:selector:object: метода можно создать подкласс NSThread и переопределить его main метод. Вы должны использовать перегруженную версию метода для реализации основной точки входа вашего потока.

Использование POSIX потоков

Mac OS X и iOS обеспечивают на C-основанную поддержку для создания потоков, использующих API POSIX. Эта технология действительно может быть использована в любом типе приложения (в том числе Cocoa и Cocoa Touch) и это может быть более удобно, если вы пишете программы для различных платформ. Процедура POSIX, которую вы используете для создания потоков называется, pthread_create.

Пример ниже показывает две пользовательских функции для создания потока с использованием POSIX вызовов. LaunchThread функция создает новый поток, главная процедура осуществляется в PosixThreadMainRoutine функции. Поскольку POSIX создает потоки, как объединяемые по умолчанию, этот пример изменяет атрибуты потока создавая отдельный поток. Маркировка потока, как отделенный, дает системе возможность вернуть себе ресурсы этого потока сразу после его выхода.

#include <assert.h>

#include <pthread.h>

 

void* PosixThreadMainRoutine(void* data)
{
    // Код, делающий какую либо работу

    return NULL;
}


void LaunchThread()
{

    // Создание потока, используя POSIX вызовы.

    pthread_attr_t  attr;
    pthread_t  posixThreadID;
    int  returnVal;

    returnVal = pthread_attr_init(&attr);
    assert(!returnVal);
    returnVal = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    assert(!returnVal);

    int  threadError = pthread_create(&posixThreadID, &attr, &PosixThreadMainRoutine, NULL);

    returnVal = pthread_attr_destroy(&attr);
    assert(!returnVal);
    if (threadError != 0)
    {
         // Сообщение об ошибке.

    }
}

Для того, чтобы поток знал, что предстоит ему сделать, вы можете передать ему указатель на некоторые данные на момент создания. Вы предоставляете этот указатель в качестве последнего параметра функции pthread_create.

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

Использование NSObject для порождения потока

В iOS и Mac OS X 10.5 и выше, все объекты обладают способностью порождать новый поток и использовать его для выполнения одного из своих методов. Метод performSelectorInBackground:withObject: создает новый отделенный поток и использует указанный метод в качестве отправной точки для нового потока. Например, если у вас есть объект (в лице переменная myObj), и объект имеет метод doSomething, и вы хотите запустить его в фоновом режиме, вы можете использовать следующий код, чтобы сделать это:

[myObj performSelectorInBackground:@selector(doSomething) withObject:nil];

Эффект вызова этого метода такой же, как если бы вы вызвали detachNewThreadSelector:toTarget:withObject: метод NSThread с текущим объектом, селектором, и параметром объекта в качестве параметров. Новый поток будет порожден сразу, используя настройки по умолчанию и начинает работать. Внутри селектора, вы должны настроить поток так же, как любой другой поток. Например, вам нужно будет настроить autorelease пул (если вы не используете сборщика мусора) и настроить цикл выполнения потока, если вы планируете его использовать.

Использование других потоковых технологий

Хотя POSIX процедуры и NSThread класс рекомендуемые к использованию технологии для создания низкого-уровневых потоков, есть другие C-технологии, доступные в Mac OS X. Из них только одну вы можете рассмотреть в качестве возможности использования это Многопроцессорные сервисы, которая сама по себе реализована на основе потоков POSIX. Многопроцессорные сервисы были разработаны специально для более ранних версий Mac OS и позже была доступна для Carbon приложений в Mac OS X. Если у вас есть существующий код, который использует эту технологию, вы можете продолжать ее использовать, хотя вы должны перенести ваш связанный с потоками код, в POSIX. Эта технология не доступна в iOS.

Использование POSIX потоков в Cocoa приложениях

Несмотря на то, что NSThread класс является основным интерфейсом для создания потоков в Cocoa приложениях, вы можете использовать POSIX потоки, если для вас это более удобно. Например, вы можете использовать POSIX потоки, если у Вас уже есть код, который использует их, и вы не хотите его переписывать. Если вы планируете использовать POSIX потоки в Cocoa приложениях, вы все равно должны быть осведомлены о взаимодействии между Cocoa и потоками и соблюдать руководящие принципы в следующих разделах.

Защита Cocoa Frameworks

Для многопоточных приложений, Cocoa frameworks используют замки и другие формы внутренней синхронизации для обеспечения правильного поведения. Во избежание снижения производительности от замков, в однопоточных случаях, Cocoa не создает их, пока приложение не запустит свой первый новый поток, используя NSThread класс. Если у вас появляются потоки, использующие только POSIX процедуры, Cocoa не получает уведомления из которых можно понять, что приложение теперь многопоточное. Когда это произойдет, операции, связанные с Cocoa структурами могут дестабилизировать или привести к сбою приложения.

Чтобы позволить Cocoa узнть, что вы собираетесь использовать несколько потоков, все, что вам нужно сделать, это породить один поток, используя NSThread класс и сразу же из него выйти. Вашей точке входа в потоке не нужно ничего делать. Просто самого факта запуска потока, с использованием NSThread достаточно для того, чтобы замки, необходимые для Cocoa frameworks встали на место.

Для проверки режима многопоточности приложения, используйте метод isMultiThreaded класса NSThread.

Смешивание POSIX и Cocoa замков

Можно с уверенностью использовать смесь POSIX и Cocoa замков внутри одного приложения. Блокировки Cocoa и условия объектов, по существу просто обертки для POSIX семафоров и условий. Для заданной блокировки, однако, вы всегда должны использовать тот же интерфейс для создания и редактирования, что и замок. Другими словами, вы не можете использовать объект Cocoa NSLock для манипулирования мьютексом, созданным с помощью функции pthread_mutex_init, и наоборот.

Настройка атрибутов потоков

После создания потока, а иногда и раньше, вы можете настроить различные части среды потока. Далее следует описание некоторых изменений, которые можно сделать, и момент времени когда это возможно.

Настройка размера стека потока

Для каждого нового созданнного потока, система выделяет определенное количество памяти в пространстве процесса в качестве стека для этого потока. Стек управляет кадрами стека, а также любыми локальными переменными для этого потока, которые были объявлены. Объемы памяти, выделяемойй для потока перечислены в "Расходах на потоки".

Если вы хотите изменить размер стека заданного потока, то необходимо сделать это до создания потока. Все потоковые технологии обеспечивают какой-либо способ установки размера стека, хотя установка размера стека с использованием NSThread доступна только в iOS и Mac OS X 10.5 и более поздних. В следующей таблице перечислены различные варианты для каждой технологии.

ТехнологияВарианты
CocoaВ iOS и Mac OS X 10.5 и выше, разместите и инициализируйте NSThread объект (без использования detachNewThreadSelector:toTarget:withObject: метода). Перед вызовом метода start объекта потока, используйте метод setStackSize:, чтобы указать новый размер стека.
POSIXСоздание новой структуры pthread_attr_t и использование pthread_attr_setstacksize функции, чтобы изменить размер стека по умолчанию. Передайте атрибуты функции pthread_create при создании потока.
Multiprocessing ServicesПередайте соответствующее значение размера стека функции MPCreateTask при создании потока.

Настройка локальной памяти потока

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

Cocoa и POSIX хранят потоковые словари по-разному, так что вы не можете смешивать и сочетать вызовы этих двух технологий. В Cocoa, можно использовать метод threadDictionary объекта NSThread для получения NSMutableDictionary объекта, в который вы можете добавить любые ключи требующиеся вашему потоку. В POSIX, можно использовать pthread_setspecific и pthread_getspecific функции для установки и получения ключей и значений вашего потока.

Установка отделенного состояния потока

Большинство высоко-уровневых потоковых технологий, создают потоки отделенными по умолчанию. Также в большинстве случаев, отделенные потоки являются более предпочтительными, поскольку они позволяют системе, освободить данные структур потока сразу же после завершения потока. Отделенные потоки также не требуют явного взаимодействия с вашей программой. Средства получения результатов из потока остаются на вашем усмотрении. Для сравнения, система не вернет ресурсы для объединяемых потоков, пока другой поток явно присоединен к этому потоку, процесс может блокировать поток, который осуществляет соединение.

Можно рассматривать объединяемые потоки как дочерние. Хотя они по-прежнему работают в качестве независимых потоков, объединяемые потоки должны быть присоединены к другому потоку, прежде чем его ресурсы могут быть возвращены в систему. Объединяемые потоки также обеспечивают явные способы передачи данных при выходе потока, в другой поток. Незадолго до своего выхода, объединяемый поток может передать указатель на данные или иные данные или свойства для возвращения в функции pthread_exit. Другой поток может претендовать на эти данные, вызовом функции pthread_join.

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

Если вы хотите создать объединяемые потоки, единственный способ сделать это является использование POSIX потоков. POSIX создает потоки, как объединяемые по умолчанию. Чтобы пометить поток, как отдельный или совмещенный, измените атрибуты потока с помощью функции pthread_attr_setdetachstate до создания потока. После того, как поток начинает свое выполнение, вы можете изменить объединяемые потоки на отделенные, вызывая функцию pthread_detach.

Установка приоритета потока

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

Важно! Как правило, хорошей идеей будет оставить приоритет вашего потока в значении по умолчанию. Увеличение приоритетов некоторых потоков также повышает вероятность голода среди более низко приоритетных потоков. Если ваше приложение содержит приоритетные и низко приоритетные потоки, которые должны взаимодействовать друг с другом, голодание более низко приоритетных потоков может блокировать другие потоки и создать узкие места в производительности.

Если вы хотите изменить приоритеты потоков, как Cocoa, так и POSIX обеспечивают способ сделать это. Для Cocoa потоков, вы можете использовать setThreadPriority: метод класса NSThread, чтобы установить приоритет потока на данный момент. Для потоков POSIX, вы используете pthread_setschedparam функцию.

Написание Вашей входной точки в поток

Создание Autorelease пулов

Приложения, ссылающиеся в Objective-C frameworks обычно должны создать по крайней мере один autorelease пул в каждом потоке. Если приложение использует управляемую модель, когда приложение обрабатывает сохранение и освобождение объектов, autorelease пул ловит любые объекты, которые autoreleased в этом потоке.

Если приложение использует сборку мусора, а не управляемую модель памяти, создание autorelease пулов не является строго обязательным. Наличие autorelease пула в приложении со сбором мусора не вредно, и по большей части просто игнорируется. Допускается в случаях, когда программный модуль должен поддерживать как сбор мусора, так и управляемую модель памяти. В таком случае, autorelease пулы должны присутствовать для поддержки управляемого кода модели памяти и просто игнорируется, если приложение запускается с включенной сборкой мусора.

Если ваше приложение использует управляемую модель памяти, создание autorelease пула должно быть первым шагом при написании кода потока. Точно так же, уничтожение этого autorelease пула должно быть последним шагом в потоке. Этот пул обеспечивает отлов autoreleased объектов, хотя и не освобождает их, пока поток не выйдет. Листинг ниже показывает структуру основной точки входа в обычный поток, который использует autorelease пул.

- (void)myThreadMainRoutine
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // пул верхнего уровня

    // работа потока ...

    [pool release];  // Освобождаем объекты в пуле
}

Из-за высокого уровня autorelease пул не освобождает объекты, пока поток не выйдет, долгоживущие потоки должны создавать дополнительные autorelease пулы, освобождающие объекты чаще. Например, поток, который использует цикл выполнения может создавать и выпускать autorelease пул каждый раз, через проход работы цикла. Слишком частое освобождение объектов из памяти вашего приложения может привести к проблемам с производительностью. Как и с любым, связанным с производительностью поведением, вы должны измерить реальную производительность вашего кода и настраивать использование autorelease пулов соответственно.

Установка обработчика исключений

Если ваше приложение перехватывает и обрабатывает исключения, ваш код в потоке должен быть готов поймать все исключения, которые могут возникнуть. Хотя лучше всего - это обработка исключений в точке, где они могут возникнуть, неспособность поймать возбужденное исключение в потоке вынуждает ваше приложение завершить работу. Установка окончательных try/catch в вашей точке входа потока позволяет ловить неизвестные исключения и обеспечить соответствующие меры реагирования.

Вы можете использовать C++ или Objective-C, стиль обработки исключений при создании проекта в Xcode. Информацию по исключениям Objective-C см. в разделе "Исключения в языке Objective-C".

Настройка цикла выполнения

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

Mac OS X и iOS обеспечивают встроенную поддержку для реализации цикла выполнения в каждом потоке. Cocoa, Carbon, и UIKit начинают цикл выполнения главного потока приложения автоматически, но если вы создаете любые вторичные потоки, вы должны настроить циклвыполнения и запустить его вручную.

Прерывание потока

Рекомендуемый способ выхода поток, чтобы она вышел из своей точки входа нормально. Хотя Cocoa, POSIX, и Multiprocessing Services предлагают процедуры для прерывания потоков напрямую, использование такой процедуры не рекомендуется. Убийство потока не дает потоку уборать после себя мусор. Потенциально может произойти утечка памяти и любых других ресурсов, используемых в настоящее время в потоке, поскольку он не может быть очищен должным образом, создавая потенциальные проблемы в дальнейшем.

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

Пример проверки на выход в течение длительной работы

- (void)threadMainPoint
{
    BOOL moreWorkToDo = YES;
    BOOL exitNow = NO;
    NSRunLoop* runLoop = [NSRunLoop currentRunLoop];

    // Добавляем exitNow BOOL в словарь потока.

    NSMutableDictionary* threadDict = [[NSThread currentThread] threadDictionary];
    [threadDict setValue:[NSNumber numberWithBool:exitNow] forKey:@"ThreadShouldExitNow"];

    // Установка источника входа.

    [self myInstallCustomInputSource];

    while (moreWorkToDo && !exitNow)
    {
        // Выполните один кусок большого объема работы здесь.
        // Измените булево значение moreWorkToDo, когда закончите.

        // Выполните цикл выполнения
        [runLoop runUntilDate:[NSDate date]];

        // Проверьте, если обработчик источника изменил exitNow значение.
        exitNow = [[threadDict valueForKey:@"ThreadShouldExitNow"] boolValue];
    }
}
close

Блок объект Читать более подробно

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

Объявление блока

Во многих случаях, можно использовать встроенные блоки, поэтому вам не нужно объявлять их. Синтаксиса декларации, однако, похож на стандартный синтаксис указателей на функции, за исключением того, что вы используете вставки (^) вместо звездочки указателя (*). Например, следующий код объявляет переменную aBlock, который ссылается на блок, который требует трех параметров и возвращает значение с плавающей точкой:

float (^aBlock)(const int*, int, float);

Создание блока

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

int (^oneFrom)(int);

oneFrom = ^(int anInt) {
    return anInt - 1;
};

Закрытие точкой с запятой требуется в качестве стандарта C конца линии маркера.

Если вы явно не объявляете возвращаемое значение выражения блока, оно может автоматически выводится из содержимого блока.

Блок-изменяемые переменные

Вы можете использовать __block модификатор хранения c переменными, объявленными локально с вложениями лексической области, чтобы обозначить, что такие переменные должны быть предоставлены по ссылке в блоке и являются изменяемыми. Любые изменения находят свое отражение в окружающей лексической области, в том числе любых других блоков, определенных в той же ограждающей лексической области.

Использование блоков

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

printf("%d\n", oneFrom(10));

Обычно, передают блок в качестве аргумента функции или методу. В этих случаях часто создают блок линию.

В следующем примере определяется, содержит ли объект NSSet слово, указанное в локальной переменной и задает значение другой локальной переменной (found) в YES (и прекращает поиск), если он выполняется. В этом примере, found объявлена ​​как переменная __block.

__block BOOL found = NO;
NSSet *aSet = [NSSet setWithObjects: @"Alpha", @"Beta", @"Gamma", @"X", nil];
NSString *string = @"gamma";

[aSet enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
    if ([obj localizedCaseInsensitiveCompare:string] == NSOrderedSame) {
        *stop = YES;
        found = YES;
    }
}];

// В этой точке, found == YES

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

Операции сравнения

Одной из наиболее распространенных операций, которые вы выполняете с блоками в среде Cocoa сравнение двух объектов, сортировка содержимого массива. Среда выполнения Objective-C определяет блок типа NSComparator для использования этих сравнений.

 
 
homeЗаметили ошибкукарта сайта 
   Made on a Mac