Это приведет к какому-либо циклу удержания? Безопасно ли использовать?
__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);