2011-07-08 4 views
7

私はあなたが観察するオブジェクトとプロパティを登録するクラスを作成しています。プロパティがnil以外に設定されると、登録されたコールバックセレクタが呼び出されます(target-actionなど)。セレクタには3つの異なるシグネチャがあり、正しいシグネチャは登録されたタイプに応じて呼び出されます。Objective-cでランタイムを取得するには?

これはうまくいきましたが、セレクタではなくブロックを「コールバック関数」として登録する機能を追加したいと考えています。与えられたブロックの関数シグネチャを見つけ出し、与えられたブロックのタイプに応じてコールバックを違って処理することは可能ですか?例えば

- (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? 
    } 
} 

任意のアイデア?

私は特定のtypedefのブロック引数を使って異なるレジスタ関数を作ることができると知っていますが、可能であれば単なる関数を持っています。

+0

余分な引数は無視されるだけなので、本当に1または2が入っていても、3つの引数があったとしても構わないかもしれません。 – user102008

答えて

5

もしあなたがclangでコンパイルしているなら、この情報を得ることができます。 Clang block ABI specから

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

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によれば、フォーマットは文字列をコード化する標準的なobjective-cメソッドです。つまり、シグネチャの形式は文書化されていません(私が知る限り)。だから、コンパイラの更新で壊れていないと思っています。

+0

詳細情報をありがとう!したがって、情報はそこにありますが、正式には使用を認可されていません。実際には、複数の登録メソッドを使用してより粗末なAPIに固執する必要があります。それでも機能しますが、Appleが今後この情報を文書化し、ブロック署名情報を「公式に」取得するための新しいAPIを公開することを願っています。 –

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); 
} 
+0

ラッパーブロックを使ってオプションの引数を扱うことをお勧めします。私のケースでは、2つの登録されたコールバックメソッドのオーグメントがかなり異なり、このメソッドを使用することはできません。そして、私はChris Devereuxに受け入れられた答えをくれたことを申し訳なく思っています - あなたは彼とまったく同じですが、私は彼の詳細なClang情報に魅了されました。 –

1

はい私は、answeredをもう1つquestionにすることができます。

ブロック構造体は公開されているclang標準であり、AFAIKは任意の目的に使用できます。

関連する問題