Короткое практическое руководство по блокам

Блоки являются мощной функцией языка C, которая является частью разработки приложений Какао. Они подобны «закрытиям» и «лямбдам», которые можно найти на языках сценариев и языках программирования, таких как Ruby, Python и Lisp. Несмотря на то, что синтаксис и сведения о ресурсе хранения блоков могли бы на первый взгляд казаться загадочными, Вы найдете, что фактически довольно просто включить блоки в код Ваших проектов.

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

Содержание:

Почему использование блокирует?

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

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

int (^Multiply)(int, int) = ^(int num1, int num2) {
    return num1 * num2;
};
int result = Multiply(7, 4); // Result is 28.

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

Преимущество блоков как функция и параметры метода состоит в том, что они позволяют вызывающей стороне обеспечить код обратного вызова при вызове. Поскольку этот код не должен быть реализован в отдельном методе или функции, Ваш код реализации может быть более простым и легким для понимания. Возьмите уведомления о NSNotification разнообразие как пример. В «традиционном» подходе объект добавляет себя как наблюдатель уведомления и затем реализует отдельный метод (идентифицированный селектором в addObserver:.. метод) для обработки уведомления:

- (void)viewDidLoad {
   [super viewDidLoad];
    [[NSNotificationCenter defaultCenter] addObserver:self
        selector:@selector(keyboardWillShow:)
        name:UIKeyboardWillShowNotification object:nil];
}
 
- (void)keyboardWillShow:(NSNotification *)notification {
    // Notification-handling code goes here.
}

С addObserverForName:object:queue:usingBlock: метод можно консолидировать обрабатывающий уведомление код с вызовом метода:

- (void)viewDidLoad {
    [super viewDidLoad];
    [[NSNotificationCenter defaultCenter] addObserverForName:UIKeyboardWillShowNotification
         object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
             // Notification-handling code goes here. 
    }];
}

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

Блоки в системной платформе APIs

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

Следующие разделы описывают каждый из этих случаев. Но прежде чем мы добираемся, к которому, вот быстрый обзор интерпретации блочных объявлений в методах платформы. Рассмотрите следующий метод NSSet класс:

- (NSSet *)objectsPassingTest:(BOOL (^)(id obj, BOOL *stop))predicate

Блочное объявление указывает, что метод передает в блок (для каждого перечислимого элемента) объект с динамическим контролем типов и булево значение ссылкой; блок возвращает булево значение. (Для чего эти параметры и возвращаемое значение фактически, покрыты Перечислением.) При указании блока, начните с каре (^) и заключенный в скобки список аргументов; следуйте за этим с самим блочным кодом, включенный фигурными скобками.

[mySet objectsPassingTest:^(id obj, BOOL *stop) {
    // Code goes here: Return YES if obj passes the test and NO if obj does not pass the test.
}];

Завершение и обработчики ошибок

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

UIView класс имеет несколько методов класса для анимаций и переходов представления, имеющих блочные параметры обработчика завершения. (Анимация представления и Переходы дают обзор этих методов.) Пример в Перечислении 1-1 показывает реализацию animateWithDuration:animations:completion: метод. Обработчик завершения в этом примере сбрасывает анимированное представление назад к его исходному расположению и прозрачности (альфа) значение спустя несколько секунд после того, как закончится анимация.

Перечисление 1-1A блока обработчика завершения
- (IBAction)animateView:(id)sender {
    CGRect cacheFrame = self.imageView.frame;
    [UIView animateWithDuration:1.5 animations:^{
        CGRect newFrame = self.imageView.frame;
        newFrame.origin.y = newFrame.origin.y + 150.0;
        self.imageView.frame = newFrame;
        self.imageView.alpha = 0.2;
    }
                     completion:^ (BOOL finished) {
                         if (finished) {
                             // Revert image view to original.
                             self.imageView.frame = cacheFrame;
                             self.imageView.alpha = 1.0;
                         }
    }];
}

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

Обработчики уведомления

NSNotificationCenter метод addObserverForName:object:queue:usingBlock: позволяет Вам реализовать обработчик для уведомления в точке, Вы устанавливаете наблюдение. Перечисление 1-2 иллюстрирует, как Вы могли бы вызвать этот метод, определив блочный обработчик для уведомления. Как с методами обработчиков уведомления, NSNotification объект передается в. Метод также берет NSOperationQueue экземпляр, таким образом, Ваше приложение может указать контекст выполнения, на котором можно выполнить блочный обработчик.

Перечисление 1-2Adding объект как наблюдатель и обработка уведомления с помощью блока
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    opQ = [[NSOperationQueue alloc] init];
    [[NSNotificationCenter defaultCenter] addObserverForName:@"CustomOperationCompleted"
             object:nil queue:opQ
        usingBlock:^(NSNotification *notif) {
        NSNumber *theNum = [notif.userInfo objectForKey:@"NumberOfItemsProcessed"];
        NSLog(@"Number of items processed: %i", [theNum intValue]);
    }];
}

Перечисление

Классы набора платформы Основы —NSArray, NSDictionary, NSSet, и NSIndexSet— объявите методы, выполняющие перечисление определенного типа набора и указывающие блоки для клиентов к коду поставки, чтобы обработать или протестировать каждый перечислимый элемент. Другими словами, методы выполняют эквивалент конструкции быстрого перечисления:

for (id item in collection) {
    // Code to operate on each item in turn.
}

Существует две общих формы методов перечисления, берущих блоки. Первыми являются методы, имена которых начинаются enumerate и не возвращайте значение. Блок для этих методов выполняет некоторую работу над каждым перечислимым элементом. Блочным параметром второго типа метода предшествуют passingTest; этот вид метода возвращает целое число или NSIndexSet объект. Блок для этих методов выполняет тест на каждом перечислимом элементе и возвратах YEStrue если элемент проходит тест. Целочисленный или индексный набор идентифицирует объект или объекты в исходном наборе, прошедшем тест.

Код в Перечислении 1-3 вызывает NSArray метод каждого из этих типов. Блок первого метода (“передающий тест” метод) возвраты YEStrue для каждой строки в массиве, имеющем определенный префикс. Код впоследствии создает временный массив с помощью индексного набора, возвращенного из метода. Блок второго метода отрезает префикс от каждой строки во временном массиве и добавляет его к новому массиву.

Перечисление 1-3Processing перечислимых массивов с помощью двух блоков
NSString *area = @"Europe";
NSArray *timeZoneNames = [NSTimeZone knownTimeZoneNames];
NSMutableArray *areaArray = [NSMutableArray arrayWithCapacity:1];
NSIndexSet *areaIndexes = [timeZoneNames indexesOfObjectsWithOptions:NSEnumerationConcurrent
                                passingTest:^(id obj, NSUInteger idx, BOOL *stop) {
    NSString  *tmpStr = (NSString *)obj;
    return [tmpStr hasPrefix:area];
}];
 
NSArray *tmpArray = [timeZoneNames objectsAtIndexes:areaIndexes];
[tmpArray enumerateObjectsWithOptions:NSEnumerationConcurrent|NSEnumerationReverse
                           usingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
                               [areaArray addObject:[obj substringFromIndex:[area length]+1]];
}];
NSLog(@"Cities in %@ time zone:%@", area, areaArray);

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

Даже при том, что это не представляет набор, NSString класс также имеет два метода с блочными параметрами, имена которых начинаются enumerate: enumerateSubstringsInRange:options:usingBlock: и enumerateLinesUsingBlock:. Первый метод перечисляет строку единицей текста указанной гранулярности (строка, абзац, слово, предложение, и т.д.); второй метод перечисляет его с методической точностью только. Перечисление 1-4 иллюстрирует, как Вы могли бы использовать первый метод.

Перечисление 1-4Using блок для нахождения соответствия подстрок в строке
NSString *musician = @"Beatles";
NSString *musicDates = [NSString stringWithContentsOfFile:
    @"/usr/share/calendar/calendar.music"
    encoding:NSASCIIStringEncoding error:NULL];
[musicDates enumerateSubstringsInRange:NSMakeRange(0, [musicDates length]-1)
    options:NSStringEnumerationByLines
    usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
           NSRange found = [substring rangeOfString:musician];
           if (found.location != NSNotFound) {
                NSLog(@"%@", substring);
           }
      }];

Анимация представления и переходы

UIView класс в iOS 4.0 представил несколько методов класса для переходов анимации и представления, берущих блоки. Блочные параметры являются двумя видами (не, все методы берут оба вида):

  • Блоки, изменяющие свойства представления, которые будут анимированы

  • Обработчики завершения

Перечисление 1-5 показывает вызов animateWithDuration:animations:completion:, метод, имеющий оба вида блочных параметров. В этом примере анимация заставляет представление исчезнуть (путем указания нулевой альфы), и обработчик завершения удаляет его из своего суперпредставления.

Перечисление 1-5Simple анимации использования представления блокирует
[UIView animateWithDuration:0.2 animations:^{
        view.alpha = 0.0;
    } completion:^(BOOL finished){
        [view removeFromSuperview];
    }];

Прочее. UIView методы класса выполняют переходы между двумя представлениями, включая зеркальные отражения и завихрения. Вызов в качестве примера transitionWithView:duration:options:animations:completion: в Перечислении 1-6 анимирует замену подпредставления как оставленный зеркальному отражению переход. (Это не реализует обработчик завершения.)

Перечисление 1-6Implementing зеркально отраженный переход между двумя представлениями
[UIView transitionWithView:containerView duration:0.2
                   options:UIViewAnimationOptionTransitionFlipFromLeft
                animations:^{
                    [fromView removeFromSuperview];
                    [containerView addSubview:toView]
                }
                completion:NULL];

Сортировка

Платформа Основы объявляет NSComparator введите для сравнения двух элементов:

typedef NSComparisonResult (^NSComparator)(id obj1, id obj2);

NSComparator тип блока, берущий два объекта и возвращающийся NSComparisonResult значение. Это - параметр в методах NSSortDescriptor, NSArray, и NSDictionary и используется экземплярами тех классов для сортировки. Перечисление 1-7 дает пример своего использования.

Перечисление 1-7Sorting массив с помощью блока NSComparator
NSArray *stringsArray = [NSArray arrayWithObjects:
                                 @"string 1",
                                 @"String 21",
                                 @"string 12",
                                 @"String 11",
                                 @"String 02", nil];
static NSStringCompareOptions comparisonOptions = NSCaseInsensitiveSearch | NSNumericSearch |
        NSWidthInsensitiveSearch | NSForcedOrderingSearch;
NSLocale *currentLocale = [NSLocale currentLocale];
NSComparator finderSort = ^(id string1, id string2) {
    NSRange string1Range = NSMakeRange(0, [string1 length]);
    return [string1 compare:string2 options:comparisonOptions range:string1Range locale:currentLocale];
};
NSLog(@"finderSort: %@", [stringsArray sortedArrayUsingComparator:finderSort]);

Этот пример взят от Блоков, Программируя Темы.

Блоки и параллелизм

Блоки являются переносимыми и анонимными объектами, инкапсулирующими единицу работы, которая может быть выполнена асинхронно в более позднее время. Из-за этого существенного факта блоки являются центральной функцией и Grand Central Dispatch (GCD) и NSOperationQueue класс, две рекомендуемых технологии для параллельной обработки.

Для больше о GCD, NSOperationQueue, и NSOperation, см. Руководство по программированию Параллелизма.