Синтаксис для определения блока, который принимает блок и возвращает блок в Objective-C


Я нахожу в документе Apple , работающем с блоками , что синтаксис для определения блока, который возвращает результат умножения двух значений:

double (^multiplyTwoValues)(double, double);

Отличается от определения блока, который принимает другой блок в качестве аргумента и возвращает еще один блок:

void (^(^complexBlock)(void (^)(void)))(void);

Почему второй синтаксис не void (^)(void)(^complexBlock)(void (^)(void))?

2 8

2 ответа:

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

Если бы вы использовали" сложный блок", который вы определили, а затем также вызывали возвращаемый блок в той же строке, это выглядело бы так:

complexBlock(void (^argBlock)(void){ /*...*/ })();
//           ^ Argument (a literal Block) to the main Block
//                                              ^ Invocation of the returned Block

Далее, разбор c-деклараций следует так называемому"правому-левому правилу". Первый шаг это "найти идентификатор". Для вашего заявления это complexBlock.

void (^(^complexBlock)(void (^)(void)))(void);
//       |     !    | Found identifier: "complexBlock is..."
Затем посмотрите направо. Мы попали в закрывающую скобку, так что это конец объявления "единица".
void (^(^            )(void (^)(void)))(void);
//       |           ! Right parenthesis closes a part of the declaration

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

void (^(^             (void (^)(void)))(void);
//     |!            | "...a Block..."

Затем снова направо. Здесь мы находим отверстие скобка, указывающая начало списка параметров. Пропустите список параметров, поскольку вас интересует тип возвращаемого значения, но он анализируется как отдельное объявление.

void (^              (void (^)(void)))(void);
//     |             !              | "...taking something or other and returning..."

Теперь, когда мы использовали список параметров:

void (^                              )(void);
//     |                            |

Продолжайте двигаться вправо, и мы попадем в закрывающую скобку:

void (^                              )(void);
//     |                             !

Итак, снова вернемся к началу текущей части и двинемся влево, где мы найдем каретку блока.

void (^                               (void);
//    !                              | "...a Block.."

Вот ключевая часть для вашего вопрос об этой декларации:

Двигаясь влево, мы снова находим открывающую скобку, поэтому мы возвращаемся к движению вправо. Вот почему список параметров возвращаемого блока идет в конце объявления.
void (                                (void);
//   !                               | Left parenthesis closes part of declaration,
//                                     **now move rightwards again**
Пройдя через все это, остальное должно быть самоочевидным. Кстати, на странице, на которую я ссылался, есть несколько демонстраций, подобных моей, одна из которых включает в себя указатели функций. Вы также можете быть удивлены http://cdecl.org , которая представляет собой онлайн-реализацию программы, которая анализирует объявления C и может помочь вам понять более тонкие разновидности.

Синтаксис блока Obj-C довольно сложен для чтения, его можно немного упростить с помощью typedefs.

//setup
typedef void (^ReturnedBlock)(void);
ReturnedBlock retBlock = ^void(void){};

typedef void (^ParamBlock)(void);
ParamBlock paramBlock = ^void(void){};

//the thing you want to do
ReturnedBlock (^someBlock)(ParamBlock) = ^ReturnedBlock(ParamBlock param){

    return retBlock;
};

//perform the block
ReturnedBlock r = someBlock(paramBlock);