5

Per me, la capacità di Objective-C di reagire, descrivere e creare problemi con l'ambiente circostante è a che punto è. Questo inizia, a un livello fondamentale, con una capacità costante di riferirsi a _cmd, in qualsiasi momento, e ottenere l'attuale SEL. Da lì, è a voi che cosa NSInvocation incantesimi o cavillo runtime si sceglie di partecipare a.Introspezionare il contesto di blocco corrente, à la _cmd all'interno di un metodo

Ora, all'interno di un blocco, si può ancora chiamare _cmd e ottenere una descrizione vaga del "contesto" attuale, vale a dire

__30-[RoomController awakeFromNib]_block_invoke123RoomController 

Descrittivo? Sì. Informativo? Ok ... Ma non così utile. Come faccio a ottenere informazioni di runtime dinamiche e accurate all'interno di un blocco, in particolare la firma chiamante, args, ecc.?

I have found a useful little method to "describe" a block in anticipo che fornisce un buon esempio del tipo di informazioni che spero di raccogliere INSIDE nel blocco.

typedef void(^blockHead)(NSString*); 
blockHead v = ^(NSString*sandy) { NSLog(@"damnDog",nil); }; 
Log([v blockDescription]); 

[v blockDescription] = <NSMethodSignature: 0x7fd6fabc44d0> 
    number of arguments = 2 
    frame size = 224 
    is special struct return? NO 
    return value: -------- -------- -------- -------- 
     type encoding (v) 'v' 
     flags {} 
     modifiers {} 
     frame {offset = 0, offset adjust = 0, size = 0, size adjust = 0} 
     memory {offset = 0, size = 0} 
    argument 0: -------- -------- -------- -------- 
    type encoding (@) '@?' 
    flags {isObject, isBlock} 
    modifiers {} 
    frame {offset = 0, offset adjust = 0, size = 8, size adjust = 0} 
    memory {offset = 0, size = 8} 
argument 1: -------- -------- -------- -------- 
    type encoding (@) '@"NSString"' 
    flags {isObject} 
    modifiers {} 
    frame {offset = 8, offset adjust = 0, size = 8, size adjust = 0} 
    memory {offset = 0, size = 8} 
     class 'NSString' 
+1

non si può davvero sapere nulla del blocco stesso all'interno del blocco a meno che non si possa fare riferimento ad esso in qualche modo. A proposito, perché vuoi sapere queste informazioni? –

+0

Come proliferano le API dei blocchi. Spesso è difficile dire al chiamante del blocco, ecc. Il compilatore consente le firme non corrispondenti ...numeri errati di argomenti e più metodi, con lo stesso nome, ma diversi tipi di blocchi, ecc. = coesistere senza lamentarsi ... Sarebbe bello sapere, a volte, cosa sta realmente accadendo ... non solo cosa "Penso che" stia accadendo. –

+1

è possibile controllare il tipo del blocco prima di chiamarlo, ma non si può fare molto all'interno del blocco perché è già chiamato possibilmente con parametri errati –

risposta

5

Se si scava abbastanza in profondità, è effettivamente possibile con un gruppo specifico di destinazione.

Ci sono tre principali architetture sarete esecuzione di codice Objective-C su, che sono:

  • x86: simulatore iOS, e antichi Mac
  • x86_64: Mac OSX
  • ARM: dispositivi iOS .

utilizzando il debugger lldb, insieme a un sacco di hacking, sono venuto su con i registri che sono in uso per ciascuna piattaforma (per tenere il puntatore del blocco):

  • x86: ecx/edi
  • x86_64: rcx/rdi
  • ARM: r0/r4

Su tutte le piattaforme, i valori sembrano essere in due registri separati, forse uno dal punto di chiamata e uno dall'argomento passato.

Utilizzando queste informazioni, ho fatto un paio di macro che funzionano sia con GCC e Clang per ottenere i valori di detti registri in una variabile C:

#if TARGET_CPU_X86_64 
// OSX, the block pointer is in the register 'rcx'. 
// The 'mov' instruction does not clobber the register, 
// So we can simply (ab)use that here. 
#define BLOCK_GET_SELF() ({ id __block_self_tmp; __asm__("mov %%rcx, %0" : "=r"(__block_self_tmp)); __block_self_tmp; }) 
#elif TARGET_CPU_X86 
// iOS Simulator, the block pointer is in the register 'ecx'. 
// Same deal as with x86_64 code, except it's in a 32-bit register. 
#define BLOCK_GET_SELF() ({ id __block_self_tmp; __asm__("mov %%ecx, %0" : "=r"(__block_self_tmp)); __block_self_tmp; }) 
#elif TARGET_CPU_ARM64 
// iOS Device, ARM64 (iPhone 5S, iPad Mini 2, iPad Air). 
// The block pointer is in the x0 register, and the x4 register. 
// Similar code to the TARGET_CPU_ARM function. 
#define BLOCK_GET_SELF() ({ id __block_self_tmp; __asm__("str x0, [%0]" :: "r"(&__block_self_tmp)); __block_self_tmp; }) 
#elif TARGET_CPU_ARM 
// iOS Device, the block pointer is in register 'r0'. 
// The 'mov' (move) instruction clobbers the r0 register 
// (which messes up the debugger) for whatever reason, 
// so we use the 'str' (store) instruction instead. 
#define BLOCK_GET_SELF() ({ id __block_self_tmp; __asm__("str r0, [%0]" :: "r"(&__block_self_tmp)); __block_self_tmp; }) 
#endif 

void blockTest() { 
    __block void *blockPtr = NULL; 
    void (^myBlock)() = ^{ 
     id this = BLOCK_GET_SELF(); 

     printf("this is:\t\t0x%.8lx\n", (uintptr_t) this); 
     printf("blockPtr is:\t0x%.8lx\n", (uintptr_t) blockPtr); 
    }; 

    // example using dispatch 
    blockPtr = (__bridge void *) myBlock; 
    dispatch_async(dispatch_get_main_queue(), myBlock); 
} 

uscita, iPhone 5 con iOS 7 Beta 2 :

 
this is:  0x17e7c890 
blockPtr is: 0x17e7c890 

Sentiti libero di farmi sapere di eventuali problemi con questo codice, e spero che ti aiuti!

Problemi correlati