2013-03-14 2 views
3

Анализатор clang может проверять наличие возвращаемой стековой памяти.Возвращающийся блок, который живет в локальном стеке

dispatch_block_t getPrintBlock (const char *msg) { 
    return ^{ 
     printf("%s", msg); 
    }; 
} 

поднимает эту ошибку: returning block that lives on the local stack

Что означает эта ошибка?

+0

Я предполагаю, что это означает, что все, что вы возвращаетесь не значение, которое сохраняется после возвращения функции ... – Floris

+0

См. http://stackoverflow.com/questions/6147940/blocks-and-stack – sfstewman

ответ

5

Вам нужно сделать копию блока, чтобы переместить его в кучу.

т.е. что-то вроде:

dispatch_block_t createPrintBlock (const char *msg) { 
    return Block_copy(^{ 
     printf("%s", msg); 
    }) ; 
} 
+0

Почему предпочитаете '-copy' над' Block_copy'? –

+1

@RosePerrone Почему бы и нет? Это зависит от контекста, но вы правы, что внутри функции C «Block_copy» было бы лучше. – Sulthan

+0

Согласен ... исправлено. Благодарю. – nielsbot

8

ошибка означает, что вы возвращаете значение, которое будет недействительным после возвращения метода. Это не только проблема с блоками, рассмотреть следующие вопросы:

- (int *) badMethod 
{ 
    int aLocalIntVariable; 

    return &aLocalIntVariable; // return a reference to aLocalIntVariable, but that variable is about to die... 
} 

Локальные переменные создаются, когда метод ввода, то место, где они живут, называется «стек». Когда метод возвращает эти локальные переменные, они уничтожаются. Вы можете вернуть значение в такую ​​переменную, но вы не можете вернуть ссылку самой переменной - она ​​будет недействительной. Вы можете передать ссылку на локальную переменную на метод, который вы вызываете, поскольку ваша локальная переменная все еще существует в этом случае.

В вашем случае вы создали блок. Objective-C выполняет создание значений блоков в стеке, то есть эффективно в анонимной локальной переменной и ссылается на них с использованием ссылки. Вы можете передать такую ​​ссылку на метод, который вы вызываете, но вы не можете его вернуть - анонимная локальная переменная будет уничтожена так же, как и любая другая.

Однако Objective-C предоставляет вам два способа создания копии значения блока как объекта, который живет на «куче» и который переживет его создателя. Во-первых есть Block_copy, который является функцией:

<reference to heap allocated block> = Block_copy(<reference to stack allocated block>); 

Это оригинальный способ сделать это и поддерживается каждый - в том числе и в чистом коде C, блоки являются частью C, а не только Objective-C. Второй способ притворяется блока является объектом уже и позволяет передавать стандартное copy сообщения:

<reference to heap allocated block> = [<reference to stack allocated block> copy]; 

Если вы в первую очередь Objective-C люди этого второй метод, вероятно, чувствует себя более комфортно, но размывает вопрос о том, почему это необходимо в первую очередь.

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

Добавление: ARC

Последний абзац выше был запрошен @newacct и длинный Q & Комментарий обмена привели. В попытке сделать информацию в этом легче следовать, мы оба удалили наши комментарии, и я обобщил эту информацию здесь как добавление.

В понимании того, как ARC обрабатывает блоки двух документов полезны:

  1. Objective-C Automatic Reference Counting, в частности, разделы 3 (блоки указателей объектов сохран), 3.2.3 (типы объектов могущий быть удержанным действуют через обратные границы) и 7.5 (правила для копирования блоков).
  2. Transitioning to ARC Release Notes, в частности пункт часто задаваемых вопросов «Как блоки работают в ARC?»

Из них можно определить, что большую часть времени ARC будет обрабатывать все копирование блоков из стека до кучи в рамках управления всех типов объектов.

Вторая ссылка выделяет один случай, который по крайней мере на момент написания документа не обрабатывался автоматически. В этом случае блок, выделенный стеком, передается параметру метода типа id, например. что-то вроде:

- (void) takeBlockWithTypeLoss:(id)block { ... } 

[obj takeBlockWithTypeLoss:^{ ... }]; 

В таких случаях, когда документ был написан, ARC не копировал блок. Если вызываемый метод выполняет операцию, которая сохраняет переданный блок, возникает проблема, так как значение сохранения не находится в куче. (Обратите внимание, что блок должен быть стеком, выделенным для возникновения проблемы. Буквенные блоки, которые не ссылаются на переменные в своей среде, статически распределены, а также литеральный блок, который сначала сохраняется в локальной переменной с сильным владением по умолчанию, а затем передается к способу будут скопированы.)

Этот случай является примером потери типа, значение, известное как тип блока, передается как id, теряющее информацию о типе. Компилятор всегда может определить такие точки, поэтому почему (или сделал ..) ARC не копирует блок? Ответ, данный в прошлом, просто один из эффективности, копия может быть не указана, и большое количество ненужных копий является хитом производительности.

Однако текущий компилятор (Xcode 4.6.1) появляется обрабатывать этот один оставшийся случай, в точке потери типа блок-копируется в кучу. Если кто-нибудь может показать, что это теперь задокументировано (или вы уверены, что ваш компилятор справляется с этим случаем, например, путем кодирования проверки), тогда будет отображаться Block_copy() (или [block copy]), если не тогда, когда происходит потеря типа, это должно быть используемый.

Добавление: июнь 2013

Как показали this question есть случай, что Xcode 4.6.3/Clang 4.2 делает не ручку. Когда блок передается как один из аргументов переменной в вариационный метод, компилятор автоматически не продвигает блок стека в кучу. Это подслучае потери типа, упомянутое выше.

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

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

(BTW. older question упоминается в комментарии к вышеупомянутому вопросу в настоящее время один из случаев, предусмотренных в спецификации и обрабатываются правильно компилятором.)

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