2013-06-03 8 views
7

Я пытаюсь передать аргументы блока 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]; 

блоки передаются в цепи следующим образом:

NSInvocationNSProxy (NSInvocation использованием forwardInvocation:) → method1methodN

methodN в конечном итоге вызывает блок успеха или неудачи в зависимости от HTTP ответ.

Нужно ли копировать блок на каждом этапе? В приведенном выше примере речь шла о первых NSInvocation. Нужно ли мне также [invocation retainArguments]; на каждом подходящем этапе? Я использую ARC.

ответ

8

Block_copy, и действительно [block copy] экземпляры. Они не волшебным образом переключают оригинал с копией в том же месте. Таким образом, по крайней мере, я думаю, что вы хотите:

successBlock = Block_copy((__bridge void *)successBlock); 
errorBlock = Block_copy((__bridge void *)errorBlock); 

(или, что то же самое, successBlock = [successBlock copy]; ...)

В противном случае вы создаете копии, ничего не делая с ними и до сих пор проходите оригиналы от призвания.

EDIT: так, я поставил следующий код в проект:

@interface DummyClass: NSObject 
@end 

typedef void (^ successBlock)(id successResponse); 
typedef void (^ failureBlock)(NSError *error); 

@implementation DummyClass 

- (id)init 
{ 
    self = [super init]; 

    if(self) 
    { 
     SEL selector = @selector(someMethodWithSuccess:failure:); 
     id target = self; 

     // 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 ... 
      NSLog(@"Off, off, off with %@", successResponse); 
     }; 

     void (^errorBlock)(NSError *error) = ^(NSError *error) { 
      // Some failure code here ... 
      NSLog(@"Dance, dance, dance till %@", error); 
     }; 

     successBlock = [successBlock copy]; 
     errorBlock = [errorBlock copy]; 

     // 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. 
     double delayInSeconds = 2.0; 
     dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); 
     dispatch_after(popTime, dispatch_get_main_queue(), 
     ^{ 
      [invoc invoke]; 

     }); 
    } 

    return self; 
} 

- (void)someMethodWithSuccess:(successBlock)successBlock failure:(failureBlock)failureBlock 
{ 
    NSLog(@"Words:"); 
    successBlock(@[@"your", @"head"]); 
    failureBlock([NSError errorWithDomain:@"you're dead" code:0 userInfo:nil]); 
} 

@end 

И добавил следующее к концу application:didFinishLaunchingWithOptions::

DummyClass *unusedInstance = [[DummyClass alloc] init]; 

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

2013-06-02 20:11:56.057 TestProject[3330:c07] Words: 
2013-06-02 20:11:56.059 TestProject[3330:c07] Off, off, off with (
    your, 
    head 
) 
2013-06-02 20:11:56.060 TestProject[3330:c07] Dance, dance, dance till Error Domain=you're dead Code=0 "The operation couldn’t be completed. (you're dead error 0.)" 
+0

Я попробовал successBlock = [successBlock cop у]; и errorBlock = [errorBlock copy]; но я получаю тот же крах с этой ошибкой: адрес не содержит раздел, который указывает на раздел в объектном файле . Как я уже упоминал, добавление строк Block_copy, как упоминалось, предотвращает крах, но я не уверен, что они утечки памяти. – pshah

+0

'Block_copy', который вы используете в настоящее время, не имеет документального эффекта. Все, что вы видите, это то, что неопределенные результаты, вызванные проблемным 'invoke', имеют другой неопределенный эффект. Это не настоящее решение. И даже зомби не помогут вам отлаживать здесь, так как они не могут сохранить объекты стека искусственно - как только стек будет расти, он перезапишет их. – Tommy

+0

У меня создалось впечатление, что вызов Block_copy заставляет блок сохранять в куче вместо стека. И, я все еще не могу понять, почему передача [successBlock copy] вместо successBlock для вызова не будет работать. – pshah

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

  • Нет связанных вопросов^_^