5

Non è raro vedere iOS consulenza per lo sviluppo lungo le linee di: blocchi di denominazione in ogg-c?

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 
    // work in background 
    NSLog(@"%s work", __PRETTY_FUNCTION__); 
    dispatch_async(dispatch_get_main_queue(), ^{ 
     // update UI on main queue 
     NSLog(@"%s updateUI", __PRETTY_FUNCTION__); 
    }); 
}); 

Questo è grande, ma può essere difficile da eseguire il debug quando qualcosa va storto. Guardando l'output:

AppName[1051:4013] __47-[Classname methodName]_block_invoke_0 work 
AppName[1051:907] __block_global_0 updateUI 

La prima riga del registro ha i nomi di classe e metodo in modo abbiamo qualche speranza di rintracciare un problema nel blocco esterno (si spera non abbiamo definito molti blocchi in questo metodo), ma la seconda linea di log (dal blocco interno)? Buona fortuna, soprattutto se hai usato questo schema molto nella tua app.

C'è un modo per fornire i nomi dei blocchi che ci aiuteranno a identificare le loro posizioni di origine nell'output della console e nei registri degli arresti anomali?

+1

In alternativa, è possibile impostare un punto di interruzione nel blocco e il debugger emetterà una traccia stack quando viene raggiunta (che è IME più utile di 'NSLog' nella maggior parte dei casi). –

+0

Anche io uso i breakpoint e non mi sono mai sentito svantaggiato dai blocchi. – danh

+3

Non penso che ci sia un nome costruito per i blocchi, ma puoi certamente denominare le code, il che ti porta in parte. Questo si presenta nel debugger e puoi includerlo nell'istruzione NSLog. In alternativa, puoi usare anche '__FILE__' e' __LINE__'. Forse definisci la tua macro che è una combinazione di quanto sopra. – Rob

risposta

0

Una cosa che si potrebbe fare è utilizzare le funzioni anziché i blocchi e dispatch_async_f anziché dispatch_async. I compromessi sono tuttavia significativi, in quanto perdi la natura del codice inline dei blocchi e la possibilità di acquisire lo stato senza doverlo marcare attraverso un puntatore di contesto.

Si potrebbe anche solo dichiarare il blocco interno all'esterno del blocco esterno e memorizzarlo in una variabile locale. Un po 'meno conciso, ma lo taggerebbe con il nome del metodo che racchiude come il blocco esterno.

4

I blocchi, una volta copiati, diventano istanze di NSBlock, il che significa che possiamo quindi utilizzare il runtime per aggiungere ogni sorta di cose belle ad esso. Ecco un esempio:

@protocol QuietTheCompiler<NSObject> 
- (NSString*) prettyBlockDescription; 
@end 

static id beautifyBlockDescription(id block, NSString *name) 
{ 
    static void *kAssocObjectPrettyBlockDescription = "A"; 
    Class blockClass = [block class]; 

    static dispatch_once_t onceToken; 
    dispatch_once(&onceToken, ^{ 

     SEL descrSel  = @selector(description); 
     SEL prettySel = @selector(prettyBlockDescription); 
     Method descrMethod = class_getInstanceMethod(blockClass, descrSel); 
     IMP originalImpl = class_getMethodImplementation(blockClass, descrSel); 

     IMP prettyImpl = imp_implementationWithBlock(^(id self_) { 
      id value = objc_getAssociatedObject(self_, kAssocObjectPrettyBlockDescription); 
      return (value != nil)? value : originalImpl(self_, descrSel); 
     }); 

     if (class_addMethod(blockClass, prettySel, prettyImpl, method_getTypeEncoding(descrMethod))) { 
      IMP newImpl = imp_implementationWithBlock(^(id self_) { 
       return [self_ prettyBlockDescription]; 
      }); 
      class_replaceMethod(blockClass, descrSel, newImpl, method_getTypeEncoding(descrMethod)); 
     } 
    }); 

    NSString *description = [NSString stringWithFormat:@"<%@: %p name=%@>",NSStringFromClass(blockClass),block,name]; 
    objc_setAssociatedObject(block, kAssocObjectPrettyBlockDescription, description, OBJC_ASSOCIATION_RETAIN); 

    return block; 
} 


int main (int argc, const char * argv[]) 
{ 
    @autoreleasepool { 

     int (^block1)(int,NSString*) = ^(int i, NSString *fmt) { 
      return i; 
     }; 

     id blockObject1 = beautifyBlockDescription([block1 copy], @"Hello"); 

     int (^block2)(int,NSString*) = ^(int i, NSString *fmt) { 
      return i+1; 
     }; 

     id blockObject2 = [block2 copy]; 

     NSLog(@"Block 1: %@", blockObject1); 
     NSLog(@"Block 1: %@", blockObject2); 
    } 
    return 0; 
} 

Qui è l'output di questo programma:

// Sample Output 
2013-03-31 12:34:48.984 Dummy[1231:303] Block 1: <__NSGlobalBlock__: 0x1000059d0 name=Hello> 
2013-03-31 12:34:48.987 Dummy[1231:303] Block 1: <__NSGlobalBlock__: 0x100005a10> 

vorrei suggerire che si avvolge la funzione beautifyBlockDescription in una macro in modo che il codice di rilascio solo restituisce il blocco.

+0

Probabilmente troppo complicato ma molto interessante! – Sulthan

+0

È un po 'complicato, ma giocare con il runtime non è mai veramente semplice. Questo è anche il tipo di funzione che si adatta bene in una piccola libreria che viene riutilizzata più e più volte ... – aLevelOfIndirection

0

Si potrebbe provare a utilizzare https://github.com/conradev/BlockTypeDescription per migliorare la leggibilità dei registri durante lo sviluppo. Tuttavia, questa libreria sostituisce un metodo nella classe NSBlock privata, non si dovrebbe mai provare a utilizzarlo in un binario dell'appstore o sarà probabilmente respinto. Il che significa che non puoi migliorare i crashlog che ricevi da Apple.