Шрифт:
Интервал:
Закладка:
NSMutableArray *array = [[NSMutableArray alloc]
initWithObjects:@"obj1",
@"obj2", nil];
[array sortUsingComparator: ^NSComparisonResult(id obj1, id obj2) {
NSLog(@"self = %@", self);
/* Возвращаем значение для блокового объекта. */
return NSOrderedSame;
}];
}
Не внеся изменений в реализацию вашего блокового объекта, вы не сможете получить доступ к self в независимом блоковом объекте. При попытке скомпилировать данный код мы получим ошибку времени компиляции:
void (^incorrectBlockObject)(void) = ^{
NSLog(@"self = %@", self); /* self здесь не определен. */
};
Если вы хотите получить доступ к self в независимом блоковом объекте, просто передайте объект, представляемый self, вашему блоковому объекту в качестве параметра:
void (^correctBlockObject)(id) = ^(id self){
NSLog(@"self = %@", self);
};
— (void) callCorrectBlockObject{
correctBlockObject(self);
}
Этому параметру не обязательно присваивать имя self. Ему можно дать любое имя. Тем не менее если назвать этот параметр self, то можно будет просто собрать код блокового объекта позже и поместить его в реализацию метода на языке Objective-C. Не придется менять имя каждого экземпляра переменной на self, чтобы код был воспринят компилятором.
Рассмотрим объявленные свойства и посмотрим, как блоковые объекты могут получать к ним доступ. При работе со встраиваемыми блоковыми объектами можно применять точечную нотацию — она позволяет считывать информацию из объявленных свойств self или записывать в них данные. Допустим, например, что у нас в классе есть объявленное свойство типа NSString, которое называется stringProperty:
#import «AppDelegate.h»
@interface AppDelegate()
@property (nonatomic, copy) NSString *stringProperty;
@end
@implementation AppDelegate
Теперь не составляет труда получить доступ к этому свойству во встраиваемом блоковом объекте:
— (void) simpleMethod{
NSMutableArray *array = [[NSMutableArray alloc]
initWithObjects:@"obj1",
@"obj2", nil];
[array sortUsingComparator: ^NSComparisonResult(id obj1, id obj2) {
NSLog(@"self = %@", self);
self.stringProperty = @"Block Objects";
NSLog(@"String property = %@", self.stringProperty);
/* Возвращаем значение для блокового объекта. */
return NSOrderedSame;
}];
}
Но в независимом блоковом объекте нельзя использовать точечную нотацию для считывания объявленного свойства или записи информации в это свойство:
void (^correctBlockObject)(id) = ^(id self){
NSLog(@"self = %@", self);
/* Вместо этого используем метод-установщик */
self.stringProperty = @"Block Objects"; /* Ошибка времени компиляции */
/* Вместо этого используем метод-получатель. */
NSLog(@"self.stringProperty = %@",
self.stringProperty); /* Ошибка времени компиляции */
};
В данном сценарии будем пользоваться методом-установщиком и методом-получателем синтезированного свойства:
void (^correctBlockObject)(id) = ^(id self){
NSLog(@"self = %@", self);
/* Это будет работать нормально. */
[self setStringProperty:@"Block Objects"];
/* Это также будет работать нормально. */
NSLog(@"self.stringProperty = %@",
[self stringProperty]);
};
Когда дело касается встраиваемых блоковых объектов, необходимо учитывать лишь одно очень важное правило: встраиваемые блоковые объекты копируют значения для переменных в своей лексической области видимости. Если вы не понимаете, что это значит, — не волнуйтесь. Рассмотрим пример:
typedef void (^BlockWithNoParams)(void);
— (void) scopeTest{
NSUInteger integerValue = 10;
BlockWithNoParams myBlock = ^{
NSLog(@"Integer value inside the block = %lu",
(unsigned long)integerValue);
};
integerValue = 20;
/* Вызываем блок здесь после изменения
значения переменной integerValue. */
myBlock();
NSLog(@"Integer value outside the block = %lu",
(unsigned long)integerValue);
}
Мы определяем целочисленную локальную переменную и сначала присваиваем ей значение 10. Затем реализуем блоковый объект, но пока не вызываем его. После того как блоковый объект реализован, мы просто изменяем значение локальной переменной, которую затем (после того как мы его вызовем) попытается считать блоковый объект. Сразу после изменения значения локальной переменной на 20 вызываем блоковый объект. Логично предположить, что блоковый объект выведет для переменной на консоль значение 20, но этого не произойдет. Он выведет значение 10, как показано здесь:
Integer value inside the block = 10
Integer value outside the block = 20
Вот что здесь происходит. Блоковый объект сохраняет для себя копию переменной integerValue, доступную только для чтения, и делает это именно там, где реализуется блок. Напрашивается вопрос: почему же блоковый объект принимает доступное только для чтения значение переменной integerValue? Ответ прост, и мы уже дали его в этом разделе. Если у локальной переменной нет префикса __block, означающего соответствующий тип хранения, локальные переменные в лексической области видимости блокового объекта просто передаются блоковому объекту как переменные, доступные только для чтения. Следовательно, чтобы изменить это поведение, мы могли бы изменить реализацию метода scopeTest и сопроводить переменную integerValue префиксом __block, указывающим тип хранения. Это делается так:
— (void) scopeTest{
__block NSUInteger integerValue = 10;
BlockWithNoParams myBlock = ^{
NSLog(@"Integer value inside the block = %lu",
(unsigned long)integerValue);
};
integerValue = 20;
/* Вызываем блок здесь после изменения
значения переменной integerValue. */
myBlock();
NSLog(@"Integer value outside the block = %lu",
(unsigned long)integerValue);
}
Теперь, если вывести на консоль результаты после вызова метода scopeTest, мы увидим следующее:
Integer value inside the block = 20
Integer value outside the block = 20
Итак, в данном разделе мы довольно подробно рассмотрели вопросы использования переменных с блоковыми объектами. Рекомендую вам написать несколько блоковых объектов и попытаться использовать в них переменные. Присваивайте им переменные, считывайте из них информацию, чтобы лучше разобраться с тем, как в блоковых объектах применяются переменные. Перечитайте этот раздел, если случайно забудете правила, регулирующие доступ к переменным в блоковых объектах.
7.3. Вызов блоковых объектов
Постановка задачи
Вы научились создавать блоковые объекты, а теперь требуется их исполнять и получать определенные результаты.
Решение
Исполняйте ваши блоковые объекты так же, как и функции на языке C. Подробнее об этом — в подразделе «Обсуждение».
Обсуждение
В разделах 7.1 и 7.2 вы видели примеры вызова блоковых объектов. В данном разделе приводятся более конкретные примеры.
Если у вас есть независимый блоковый объект, его можно вызвать так же, как мы вызывали бы функцию на языке C:
void (^simpleBlock)(NSString *) = ^(NSString *paramString){
/* Реализуем блоковый объект и используем параметр paramString. */
};
— (void) callSimpleBlock{
simpleBlock(@"O'Reilly");
}
Если вы хотите вызвать независимый блоковый объект внутри другого независимого блокового объекта, действуйте так же, как при активизации метода на языке C:
NSString *(^trimString)(NSString *) = ^(NSString *inputString){
NSString *result = [inputString stringByTrimmingCharactersInSet:
[NSCharacterSet whitespaceCharacterSet]];
return result;
};
NSString *(^trimWithOtherBlock)(NSString *) = ^(NSString *inputString){
return trimString(inputString);
};
— (void) callTrimBlock{
NSString *trimmedString = trimWithOtherBlock(@" O'Reilly ");
NSLog(@"Trimmed string = %@", trimmedString);
}
Продолжим данный пример и вызовем метод callTrimBlock на языке Objective-C:
- QT 4: программирование GUI на С++ - Жасмин Бланшет - Программирование
- Язык программирования C#9 и платформа .NET5 - Эндрю Троелсен - Программирование
- C# для профессионалов. Том II - Симон Робинсон - Программирование
- Стандарты программирования на С++. 101 правило и рекомендация - Герб Саттер - Программирование
- Программирование игр и головоломок - Жак Арсак - Программирование
- Генерация высококачественного кода для программ, написанных на СИ - Филипп Хислей - Программирование
- Программирование на Python с нуля - Максим Кононенко - Программирование
- C# 4.0: полное руководство - Герберт Шилдт - Программирование
- Microsoft Visual C++ и MFC. Программирование для Windows 95 и Windows NT. Часть 2 - Александр Фролов - Программирование
- От «Энигмы» до ChatGPT - Рустам Агамалиев - Программирование / Экономика