5

나를 위해 Objective-C의 주변 환경에 반응하고 설명하고 혼란스럽게 처리 할 수 ​​있습니다. 이것은 기초 단계에서 언제든지 _cmd을 참조 할 수있는 확고한 능력으로 시작하여 현재 SEL을 얻습니다. 거기에서, 당신이에 참여하기 위해 무엇을 선택 NSInvocation 주술 또는 런타임 발뺌 당신에게 달려 있습니다.메서드 내에서 현재 블록 컨텍스트 인 à la _cmd를 검사합니다.

이제 블록 내부에, 당신은 여전히 ​​_cmd를 호출하고 현재의 "컨텍스트"의 막연한 설명을 얻을 수있는, 즉

__30-[RoomController awakeFromNib]_block_invoke123RoomController 

설명? 예. 정보? 오케이.하지만 그렇게 유용하지는 않습니다. 어떻게 블록, 특히 호출 서명, args, 등 내부에 역동적이고 정확한 런타임 정보를 어떻게합니까?

I have found a useful little method to "describe" a block 미리 블록 유형을 얻으려고하는 정보 유형의 좋은 예가됩니다.

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

당신이 그것을 어떻게 든 참조 할 수 없다면 당신은 블럭 안의 블럭 자체에 대해서 정말로 알 수 없습니다. 왜 이런 정보를 알고 싶습니까? –

+0

블록 API가 확산되면서 블록 호출자에게 말할 때가 종종 어렵습니다. 컴파일러는 일치하지 않는 서명을 허용합니다 ...부정확 한 수의 인수와 이름이 같지만 블록 유형이 다른 여러 메소드가 불만없이 공존 할 수 있습니다. 실제로 가끔은 무엇이 일어나고 있는지를 아는 것이 좋을 것입니다. "나는 생각한다"고 생각한다. –

+1

블록을 호출하기 전에 블록의 유형을 검사 할 수 있지만 잘못된 매개 변수로 이미 호출 되었기 때문에 블록 내부에서 많은 작업을 수행 할 수 없습니다. –

답변

5

충분히 깊게 파고 들면 일부 대상 지정 어셈블리에서 실제로 가능합니다.

세 가지입니다 당신이 목표 - C 코드를 실행할 기본 아키텍처가 있습니다

  • 86 : iOS 시뮬레이터, 고대 맥은
  • x86_64의 : 맥 OSX
  • ARM : iOS 기기 . 해킹의 많은 함께 lldb 디버거를 사용

, 나는 (블록 포인터를 보유하는) 각 플랫폼에 사용되는 레지스터와 함께 올라와있다 :

  • 86 : ecx/edi
  • x86_64의 : rcx/rdi
  • ARM : r0/r4

모든 플랫폼에서 값은 두 개의 개별 레지스터에있는 것처럼 보입니다. 아마도 호출 지점과 전달 된 인수 중 하나 일 것입니다. 이 정보를 사용하여

, 나는 C 변수로 말했다 레지스터의 값을 얻기 위해 모두 GCC와 연타와 함께 작동합니다 몇 가지 매크로를 만든 :

#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); 
} 

출력, 아이폰 5 실행 아이폰 OS 7 베타 2 :

 
this is:  0x17e7c890 
blockPtr is: 0x17e7c890 

이 코드의 문제를 알려 주시면 기꺼이 도와 드리겠습니다.