2013-06-03 16 views
7

Sto tentando di passare argomenti del blocco a NSInvocation, ma l'app si arresta in modo anomalo. L'invocazione effettua una richiesta di rete e chiama i blocchi di esito positivo o negativo. Penso che il problema è che i blocchi sono deallocati prima che la richiesta di rete finisca. Sono riuscito a farlo funzionare con alcuni hackery Block_copy e non segnala alcuna perdita utilizzando strumenti.NSInvocazione con argomenti del blocco

Domande: - È possibile che la perdita ci sia anche se l'analizzatore statico o gli strumenti non lo segnalano? - Esiste un modo migliore per "mantenere" il blocco?

// 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]; 

Aggiornamento: Ho aggiornato il codice di seguito. I blocchi sono NSMallocBlocks, ma l'app si arresta ancora in modo anomalo.

// 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]; 

I blocchi vengono passate nella catena come segue:

NSInvocationNSProxy (NSInvocation usando forwardInvocation:) → method1methodN

methodN casualmente invita il blocco successo o il fallimento seconda del HTTP risposta.

Devo copiare il blocco in ogni fase? L'esempio precedente parlava del primo NSInvocation. Ho anche bisogno di [invocation retainArguments]; ad ogni passo appropriato? Sto usando ARC.

risposta

8

Block_copy, e infatti [block copy]restituire copie. Non spostano magicamente l'originale con una copia nella stessa posizione. Così per lo meno Penso che si desidera:

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

(o, equivalentemente, successBlock = [successBlock copy]; ...)

In caso contrario si sta creando copie, non fare nulla con loro e ancora passare gli originali off per l'invocazione.

EDIT: così, ho messo il seguente codice in un progetto:

@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 

e ha aggiunto quanto segue alla fine del application:didFinishLaunchingWithOptions::

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

Il risultato è che due secondi dopo il lancio il mio programma il seguente appare sulla console:

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

Ho provato successBlock = [cop del successo y]; e errorBlock = [copia errorBlock]; ma ottengo lo stesso crash con questo errore: l'indirizzo non contiene una sezione che punta a una sezione in un file oggetto . Come ho già detto, l'aggiunta delle righe Block_copy come indicato impedisce l'arresto anomalo, ma non sono sicuro che perdano memoria. – pshah

+0

Le 'Block_copy' che si usano attualmente non hanno alcun effetto documentato. Tutto quello che state vedendo è che i risultati indefiniti causati dal 'invoke problematico 'hanno un effetto indefinito diverso. Non è una soluzione reale. E anche gli zombi non ti aiuteranno a eseguire il debug qui, dal momento che non possono mantenere artificialmente in pila gli oggetti dello stack - una volta che lo stack si ingrandisce, li sovrascriverà. – Tommy

+0

Ho avuto l'impressione che chiamare Block_copy imponga che il blocco venga salvato nell'heap anziché nello stack. E, ancora non riesco a capire perché passare in [copia di successoBlock] invece di successBlock all'invocazione non funziona. – pshah