Это приведет к какому-либо циклу удержания? Безопасно ли использовать?
__block void (^myBlock)(int) = [^void (int i)
{
if (i == 0)
return;
NSLog(@"%d", i);
myBlock(i - 1);
} copy];
myBlock(10);
myBlock = nil;
Это приведет к какому-либо циклу удержания? Безопасно ли использовать?
__block void (^myBlock)(int) = [^void (int i)
{
if (i == 0)
return;
NSLog(@"%d", i);
myBlock(i - 1);
} copy];
myBlock(10);
myBlock = nil;
В коде есть содержится цикл сохранения, но вы можете разбить цикл сохранения в конце рекурсии, установив myBlock на нуль в случае базы рекурсии (i == 0).
Лучший способ доказать это - попробовать его, работая под инструментом Allocations, с выключенным "Отменить незаписанные данные при остановке", "Включить учетные записи", отключить "Только отслеживать активные выделения".
Я создал новый проект Xcode с использованием шаблона инструмента командной строки OS X. Здесь вся программа:
#import <Foundation/Foundation.h>
void test() {
__block void (^myBlock)(int) = [^void (int i){
if (i == 0) {
// myBlock = nil;
return;
}
NSLog(@"myBlock=%p %d", myBlock, i);
myBlock(i - 1);
} copy];
myBlock(10);
}
int main(int argc, const char * argv[])
{
@autoreleasepool {
test();
}
sleep(1);
return 0;
}
Затем я запустил его под инструментом Allocations с настройками, описанными выше. Затем я изменил "Статистика" на "Консоль" в "Инструменты", чтобы увидеть выход программы:
2012-10-26 12:04:31.391 recursiveBlockTest[71789:303] myBlock=0x7ff142c24700 10
2012-10-26 12:04:31.395 recursiveBlockTest[71789:303] myBlock=0x7ff142c24700 9
2012-10-26 12:04:31.396 recursiveBlockTest[71789:303] myBlock=0x7ff142c24700 8
2012-10-26 12:04:31.397 recursiveBlockTest[71789:303] myBlock=0x7ff142c24700 7
2012-10-26 12:04:31.397 recursiveBlockTest[71789:303] myBlock=0x7ff142c24700 6
2012-10-26 12:04:31.398 recursiveBlockTest[71789:303] myBlock=0x7ff142c24700 5
2012-10-26 12:04:31.398 recursiveBlockTest[71789:303] myBlock=0x7ff142c24700 4
2012-10-26 12:04:31.399 recursiveBlockTest[71789:303] myBlock=0x7ff142c24700 3
2012-10-26 12:04:31.400 recursiveBlockTest[71789:303] myBlock=0x7ff142c24700 2
2012-10-26 12:04:31.401 recursiveBlockTest[71789:303] myBlock=0x7ff142c24700 1
<End of Run>
Я скопировал адрес блока (0x7ff142c24700), изменил "Консоль" на "Список объектов" и вставил адрес в поле поиска. Инструменты показали мне только выделение для блока:

Точка под столбцом Live означает, что блок все еще был выделен при выходе из программы. Он просочился. Я щелкнул стрелку рядом с адресом, чтобы увидеть полную историю выделения блока:

Только одно событие с этим распределением: оно было выделено.
Затем я раскомментировал строку myBlock = nil в инструкции if (i == 0). Затем я снова запустил его под профилировщиком. Система рандомизирует адреса памяти для обеспечения безопасности, поэтому я очистил панель поиска, а затем снова проверил консоль для адреса блока в этом прогоне. На этот раз это было 0x7fc7a1424700. Я снова переключился на представление "Список объектов" и вставил его в новый адрес 0x7fc7a1424700. Вот что я увидел:

На этот раз нет столбцов под столбцом Live, что означает, что к моменту выхода программы блок был освобожден. Затем я нажал на стрелку рядом с адресом, чтобы увидеть полную историю:

На этот раз блок был выделен, выпущен и освобожден.
Существует простое решение, которое позволяет избежать цикла и потенциальной необходимости преждевременной копии:
void (^myBlock)(id,int) = ^(id thisblock, int i) {
if (i == 0)
return;
NSLog(@"%d", i);
void(^block)(id,int) = thisblock;
block(thisblock, i - 1);
};
myBlock(myBlock, 10);
Вы можете добавить обертку, чтобы вернуть исходную подпись типа:
void (^myBlockWrapper)(int) = ^(int i){ return myBlock(myBlock,i); }
myBlockWrapper(10);
Это становится утомительным, если вы хотите расширить его, чтобы сделать взаимную рекурсию, но я не могу придумать вескую причину, чтобы сделать это в первую очередь (не был бы класс более ясным?).
Если вы используете ARC, у вас есть цикл сохранения, потому что __block переменные объекта сохраняются блоком. Таким образом, блок сохраняет себя. Вы можете избежать этого, объявив myBlock как __block и __weak.
Если вы используете MRC, переменные объекта __block не сохраняются, и у вас не должно быть проблем. Просто не забудьте освободить myBlock в конце.
Нет, это не вызовет цикл сохранения. Ключевое слово __block указывает блоку не копировать myBlock, который произошел до назначения, вызвавшего сбой приложения. Если это не ARC, единственное, что вам нужно сделать, это освободить myBlock после вызова myBlock(10).
Мне нужно решение, которое не получает никаких предупреждений, и в этом потоке fooobar.com/questions/260373/... Tammo Freese дает лучшее решение:
__block void (__weak ^blockSelf)(void);
void (^block)(void) = [^{
// Use blockSelf here
} copy];
blockSelf = block;
// Use block here
Его объяснение имеет смысл.
Вот современное решение проблемы:
void (^myBlock)();
__block __weak typeof(myBlock) weakMyBlock;
weakMyBlock = myBlock = ^void(int i) {
void (^strongMyBlock)() = weakMyBlock; // prevents the block being delloced after this line. If we were only using it on the first line then we could just use the weakMyBlock.
if (i == 0)
return;
NSLog(@"%d", i);
strongMyBlock(i - 1);
};
myBlock(10);