Я пытаюсь передать аргументы блока NSInvocation
, но приложение выходит из строя. Вызов вызывает сетевой запрос и вызывает блоки успеха или отказа. Я думаю, проблема заключается в том, что блоки освобождаются до завершения запроса сети. Мне удалось заставить его работать с хакером Block_copy
, и он не сообщает о каких-либо утечках с помощью инструментов.NSInvocation с аргументами блока
Вопросы: - Возможно ли, что утечка существует, даже если статический анализатор или приборы не сообщают об этом? - Есть ли лучший способ «сохранить» блок?
// Create the NSInvocation
NSMethodSignature *methodSignature = [target methodSignatureForSelector:selector];
NSInvocation* invoc = [NSInvocation invocationWithMethodSignature:methodSignature];
[invoc setTarget:target];
[invoc setSelector:selector];
// Create success and error blocks.
void (^successBlock)(id successResponse) = ^(id successResponse) {
// Some success code here ...
};
void (^errorBlock)(NSError *error) = ^(NSError *error) {
// Some failure code here ...
};
/*
Without the two Block_copy lines, the block gets dealloced too soon
and the app crashes with EXC_BAD_ACCESS
I tried [successBlock copy] and [failureBlock copy] instead,
but the app still crashes.
It seems like Block_copy is the only way to move the block to the heap in this case.
*/
Block_copy((__bridge void *)successBlock);
Block_copy((__bridge void *)errorBlock);
// Set the success and failure blocks.
[invoc setArgument:&successBlock atIndex:2];
[invoc setArgument:&errorBlock atIndex:3];
[invoc retainArguments]; // does not retain blocks
// Invoke the method.
[invoc invoke];
Обновление: я обновил код до ниже. Блоки NSMallocBlocks
, но приложение все еще падает.
// Create success and error blocks.
int i = 0;
void (^successBlock)(id successResponse) = ^(id successResponse) {
NSLog(@"i = %i", i);
// Some success code here ...
};
void (^errorBlock)(NSError *error) = ^(NSError *error) {
NSLog(@"i = %i", i);
// Some failure code here ...
};
/*** Both blocks are NSMallocBlocks here ***/
// Set the success and failure blocks.
void (^successBlockCopy)(id successResponse) = [successBlock copy];
void (^errorBlockCopy)(NSError *error) = [errorBlock copy];
/*** Both blocks are still NSMallocBlocks here - I think copy is a NoOp ***/
// Set the success and failure blocks.
[invoc setArgument:&successBlockCopy atIndex:2];
[invoc setArgument:&errorBlockCopy atIndex:3];
[invoc retainArguments]; // does not retain blocks
// Invoke the method.
[invoc invoke];
блоки передаются в цепи следующим образом:
NSInvocation
→ NSProxy
(NSInvocation
использованием forwardInvocation:
) → method1
→ methodN
methodN
в конечном итоге вызывает блок успеха или неудачи в зависимости от HTTP ответ.
Нужно ли копировать блок на каждом этапе? В приведенном выше примере речь шла о первых NSInvocation
. Нужно ли мне также [invocation retainArguments];
на каждом подходящем этапе? Я использую ARC.
Я попробовал successBlock = [successBlock cop у]; и errorBlock = [errorBlock copy]; но я получаю тот же крах с этой ошибкой: адрес не содержит раздел, который указывает на раздел в объектном файле . Как я уже упоминал, добавление строк Block_copy, как упоминалось, предотвращает крах, но я не уверен, что они утечки памяти. – pshah
'Block_copy', который вы используете в настоящее время, не имеет документального эффекта. Все, что вы видите, это то, что неопределенные результаты, вызванные проблемным 'invoke', имеют другой неопределенный эффект. Это не настоящее решение. И даже зомби не помогут вам отлаживать здесь, так как они не могут сохранить объекты стека искусственно - как только стек будет расти, он перезапишет их. – Tommy
У меня создалось впечатление, что вызов Block_copy заставляет блок сохранять в куче вместо стека. И, я все еще не могу понять, почему передача [successBlock copy] вместо successBlock для вызова не будет работать. – pshah