2010-07-15 5 views
36

Я просто думал, так как вы можете рассматривать объекты Blocks как объекты, если я создаю два из них, а затем добавить их в NSArray, есть ли способ их выполнить из массива?Выполнение блоков из NSArray?

int (^Block_001)(void) = ^{ return 101; }; 
int (^Block_002)(void) = ^{ return 202; }; 
NSArray *array = [NSArray arrayWithObjects:Block_001, Block_002, nil]; 

EDIT: Обновление для ясности Per @ davedelong отличного ответа

int (^Block_001)(void) = [^{ return 101; } copy]; 
int (^Block_002)(void) = [^{ return 202; } copy]; 
NSArray *array = [NSArray arrayWithObjects:Block_001, Block_002, nil]; 

[Block_001 release]; 
[Block_002 release]; 
+2

Хороший набор ответов .... Я видел название и надеялся на несколько простых точек реплики. Люди получили это довольно тщательно. :) – bbum

+0

Просто быстрая точка, если вы не копируете/не отпускаете блок, будет в стеке ... Итак, если стек будет уничтожен, приложение рухнет правильно? – fzaziz

ответ

29

Конечно, вы просто вызвать его с (), как и любым другим блоком, но вам нужно типаж значения, извлекать из NSArray. Вот пример (с добавленным ЬурейеЕ, потому что в противном случае у меня болит голова):

typedef int (^IntBlock)(void); 
IntBlock Block_001 = ^{ return 101; }; 
IntBlock Block_002 = ^{ return 202; }; 
NSArray *array = [NSArray arrayWithObjects:Block_001, Block_002, nil]; 
int x = ((IntBlock)[array objectAtIndex:0])(); // now x == 101 
+0

нормально, поэтому не знакомы с указателями функций, я получаю это правильно? Typedef определяет «IntBlock», который является указателем на блок, который возвращает int и не принимает аргументов. Я думаю, что понимаю, и смотрю на альтернативу (я уверен, что я ошибаюсь). Я ценю ваше решение пойти с typedef :) Очень ценю. – fuzzygoat

+0

Да, у вас это есть. –

+1

Вам не нужно бросать его. Он отлично работает, чтобы выполнить 'int (^ block)() = [array objectAtIndex: 0]'. Указатель - это указатель, в конце концов, по крайней мере, на языках низкого уровня, таких как 'C' и его производные. – aroth

6

Конечно, вы можете.

int (^x)(void) = [array objectAtIndex:0]; 
printf("%d\n", x()); // prints 101. 
+0

Я действительно считал это, очень интересным. Можете ли вы просто уточнить для меня, правильно ли я ссылаюсь на «int (^ x) (void)» как указатель на блок? Просто пытаюсь убедиться, что верю в правильность терминологии. – fuzzygoat

60

@KennyTM и @David являются правильными, но ваш код потенциально неправильно. Вот почему:

При создании объекта NSArray с объектами он будет retain объектов, помещенных в него. В случае блоков используется функция Block_retain. Это означает, что массив сохранил созданные вами блоки, но , которые живут в стеке (блоки являются одним из очень редких примеров объектов Objective-C, которые могут быть созданы в стеке, не вникая в абсурдные трюки). Это означает, что как только этот метод выйдет, ваш массив теперь указывает на мусор, потому что блоки, которые он указывал на , больше не существуют. Для того, чтобы сделать это правильно, вы должны сделать:

int (^Block_001)(void) = [^{ return 101; } copy]; 
int (^Block_002)(void) = [^{ return 202; } copy]; 
NSArray *array = [NSArray arrayWithObjects:Block_001, Block_002, nil]; 

[Block_001 release]; 
[Block_002 release]; 

Вызывая copy на блоке, вы явно перемещая блок из стека и на куче, где он может безопасно оставаться после выхода метода/функции , Затем после того, как вы добавили блоки в массив, вы должны сбалансировать copy (из-за правила NARC) с последующим вызовом release. Имеют смысл?

+0

Привет, Дейв, да, я понимаю, я не знал о создании блоков в стеке (только сейчас начал смотреть на блоки). Очень признателен. – fuzzygoat

+0

Отличная точка зрения на необходимость копирования –

+0

Привет, Дейв, я нашел этот вопрос и принятый ответ Дэвида Гельхара, поэтому я его реализовал, и он работал неплохо, это ** не ** потерпел крах. Читая далее, ваш ответ имел смысл, но не объясняет, почему код Дэвида не рушится (сразу?). У вас есть какая-то подсказка? – Tieme

Смежные вопросы