Невозможно понять лексическую область блока - PullRequest
6 голосов
/ 11 июля 2011

Чтобы понять лексическую область действия блока, я должен написать следующий код

typedef int (^ MyBlock)(void);

MyBlock b[3];

for (int i=0; i<3; i++) {
    b[i]=^{return i;};
}

for (int i=0; i<3; i++) {
    NSLog(@"%d",b[i]());
}

NSLog(@"----------------------------");

int j=0;

b[0]=^{return j;};  
j++;

b[1]=^{return j;};
j++;

b[2]=^{return j;};

for (int i=0; i<3; i++) {
    NSLog(@"%d",b[i]());
}
  1. впервые о / п 2,2,2
  2. второй раз о / п 0,1,2

Я ожидаю 2,2,2 для выполнения обоих блоков.

Может кто-нибудь объяснить, почему это так?

Ответы [ 2 ]

7 голосов
/ 11 июля 2011

Я предполагаю, что вы читали сообщение bbum о блоках и знаете, что ваш код некорректен, поскольку вы не копируете блоки из стека в кучу.

Это говорит:

for (int i=0; i<3; i++) {
    b[i]=^{return i;};
}

выполняет следующие действия в каждой итерации:

  1. Распределяет пространство в стеке для блочной переменной. Скажем, адрес его памяти A ;
  2. Создает блок в стеке и присваивает его адрес ( A ) b[i];
  3. В конце итерации, поскольку составной оператор / область действия ({}) закончился, извлекает все, что было в стеке, и сбрасывает указатель стека.

Стек увеличивается в начале каждой итерации и уменьшается в конце каждой итерации. Это означает, что все блоки создаются в одном и том же адресе памяти, а именно A . Это также означает, что все элементы в массиве b в конечном итоге указывают на один и тот же блок, а именно последний созданный блок. Вы можете проверить это, запустив следующий код:

for (int i = 0; i < 3; i++) {
    printf("%p", (void *)b[i]);
}

который должен вывести что-то вроде:

0x7fff5fbff9e8
0x7fff5fbff9e8
0x7fff5fbff9e8

Все элементы указывают на один и тот же блок, последний из которых создан в адресе памяти A = 0x7fff5fbff9e8.

С другой стороны, когда вы делаете следующее:

b[0]=^{return j;};  
j++;

b[1]=^{return j;};
j++;

b[2]=^{return j;};

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

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

0x7fff5fbff9b8
0x7fff5fbff990
0x7fff5fbff968

, показывающий, что каждый блок имеет свой адрес памяти.

0 голосов
/ 11 июля 2011

i, который вы используете для итерации массива блоков с b [i], это не i, который используется внутри каждого блока.Определяемые вами блоки ссылаются на i, который используется во время определения.И что i изменяется на 2. Затем вы перебираете эти блоки с другим i, но блок по-прежнему ссылается на исходный i (который к настоящему времени содержит значение 2), который вы использовали во время определения блока, хотя я уже "мертвый "для остальной части программы.Во втором случае ваши блоки используют также общую переменную, но вы изменяете ее перед каждым использованием.

Ключ: блок всегда связан с переменной, которая использовалась при определении.I во втором цикле for - это не i, к которому относятся блоки.

Блоки могут вызываться, когда определяющий код уже «мертв».Для этого ссылочные переменные имеют «расширенный» live.Они также могут быть перемещены в кучу во время выполнения.

Посмотрите видео WWDC 2010 "Сессия 206 - Представление блоков и Grand Central Dispatch на iPhone".

...