7

オブジェクトと観察するプロパティを登録するクラスを書いています。プロパティが 以外のものに設定されるとnil、登録されたコールバック セレクターが呼び出されます (target-action など)。セレクターには 3 つの異なるシグネチャーがあり、登録された型に応じて適切なシグネチャーが呼び出されます。

これは問題なく動作しますが、「コールバック関数」としてセレクターの代わりにブロックを登録する機能を追加したいと考えています。提供されたブロックの関数シグネチャを見つけて、提供されたブロックのタイプに応じて異なる方法でコールバックを処理することは可能ですか?

例えば:

- (void)registerCallbackBlock:(id)block
{
    if ([self isBlock:block] {
        if ([self isMethodSignatureOne:block]) { /* */ }
        else if ([self isMethodSignatureTwo:block]) { /* */ }
        else { assert(false); }  // bad Block signature

        block_ = block;  // assuming ARC code
    }
    else { assert(false); } // not a block
} 

- (void)callBlock 
{
    if ([self isMethodSignatureOne:block_] {
        block_(arg1_, arg2_);         // needs casting?
    }
    else if ([self isMethodSignatureTwo:block_) {
        block_(arg1_, arg2_, arg3_);  // needs casting?
    }
}

何か案は?

特定の 'ed Block 引数を使用してさまざまなレジスタ関数を作成できることはわかっていtypedefますが、可能であれば単一の関数を使用したいと考えています。

4

3 に答える 3

5

clang でコンパイルしている場合は、この情報を取得できます。

Clang ブロック ABI 仕様から:

ブロックの ABI は、コンパイラが必要とするレイアウトとランタイム関数で構成されます。ブロックは、次の形式の構造で構成されます。

struct Block_literal_1 {
    void *isa; // initialized to &_NSConcreteStackBlock or &_NSConcreteGlobalBlock
    int flags;
    int reserved; 
    void (*invoke)(void *, ...);
    struct Block_descriptor_1 {
        unsigned long int reserved; // NULL
        unsigned long int size;     // sizeof(struct Block_literal_1)
        // optional helper functions
        void (*copy_helper)(void *dst, void *src);     // IFF (1<<25)
        void (*dispose_helper)(void *src);             // IFF (1<<25)
        // required ABI.2010.3.16
        const char *signature;                         // IFF (1<<30)
    } *descriptor;
    // imported variables
};

次のフラグ ビットは、ABI.2010.3.16 の可能性のために使用されます。

enum {
    BLOCK_HAS_COPY_DISPOSE =  (1 << 25),
    BLOCK_HAS_CTOR =          (1 << 26), // helpers have C++ code
    BLOCK_IS_GLOBAL =         (1 << 28),
    BLOCK_HAS_STRET =         (1 << 29), // IFF BLOCK_HAS_SIGNATURE
    BLOCK_HAS_SIGNATURE =     (1 << 30), 
};

実際には、clang の現在のバージョンは署名情報をエンコードします。コメントhereによると、形式は単なる標準の object-c メソッド エンコーディング文字列です。とはいえ、署名の形式は (私が知る限り) 文書化されていないため、コンパイラの更新で壊れないとどの程度想定できるかわかりません。

于 2011-10-11T20:45:40.497 に答える
3

現在のABIではこれが実際に可能だとは思いません。ただし、次のようなことができます。

- (void)registerSimpleCallback: (BlockWith2Args)block {
    BlockWith3Args wrapper = ^ (id arg1, id arg2, id arg3) {
        block(a, b);
    };
    [self registerComplexCallback: wrapper];
}

- (void)registerComplexCallback: (BlockWith3Args)block {
    [block_ release];
    block_ = [block copy];
}

- (void)callBlock {
    if (block_)
        block_(arg1, arg2, arg3);
}
于 2011-10-11T15:19:00.407 に答える
1

はい、別の質問で答えたように、できます。

ブロック構造体は公開されたclang標準であり、私の知る限り、必要に応じて使用できます。

于 2012-06-08T22:00:39.873 に答える