5

私にとっては、Objective-C が周囲に反応し、説明し、混乱させる能力こそが、まさにその場所です。これは、基本的なレベルで、_cmdいつでも を参照し、現在の を取得する揺るぎない能力から始まりSELます。そこから、どのNSInvocation呪文や実行時のごまかしに参加するかは、あなた次第です。

現在、ブロック内では、呼び出し_cmdて現在の「コンテキスト」のあいまいな説明を取得できます。つまり、

__30-[RoomController awakeFromNib]_block_invoke123RoomController

説明的?はい。 有益ですか?わかりました...しかし、あまり役に立ちません。ブロック内の動的で正確なランタイム情報、具体的には呼び出しシグネチャ、引数などを取得するにはどうすればよいですか?

ブロックの内側で獲得したい情報のタイプの良い例を提供する、事前に ブロックを「説明」するための便利な小さな方法を見つけました。

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'
4

1 に答える 1

5

十分に深く掘り下げると、ターゲット固有のアセンブリで実際に可能です。

Objective-C コードを実行する主なアーキテクチャは次の 3 つです。

  • x86: iOS シミュレーターと古代の Mac
  • x86_64: Mac OS X
  • ARM: iOS デバイス。

lldb デバッガーと多くのハッキングを使用して、各プラットフォームで使用されている (ブロック ポインターを保持するための) レジスターを見つけました。

  • x86: ecx/edi
  • x86_64: rcx/rdi
  • アーム: r0/r4

すべてのプラットフォームで、値は 2 つの別個のレジスタにあるように見えます。おそらく、1 つは呼び出しポイントからのもので、もう 1 つは渡された引数からのものです。

この情報を使用して、GCC と Clang の両方で動作し、前述のレジスタの値を 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);
}

出力、iOS 7 Beta 2 を実行している iPhone 5:

これは: 0x17e7c890
blockPtr は: 0x17e7c890

このコードに関する問題がありましたら、お気軽にお知らせください。お役に立てば幸いです。

于 2013-07-08T15:57:19.373 に答える