Как получить метаданные типа блока времени выполнения в Objective-c? - PullRequest
7 голосов
/ 08 июля 2011

Я пишу класс, в котором вы регистрируете объект и свойство для наблюдения.Когда для свойства устанавливается значение, отличное от nil, вызывается зарегистрированный селектор обратного вызова (например, target-action).Селектор может иметь три разные подписи, и правильная вызывается в зависимости от того, какой тип был зарегистрирован.

Это прекрасно работает, но теперь я хочу добавить возможность регистрировать блок вместо селектора в качестве «функции обратного вызова».Можно ли узнать сигнатуру функции поставляемого блока и обработать обратный вызов по-разному в зависимости от типа поставляемого блока?

Например:

- (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 'ed, но я бы предпочел иметь одну функцию, если это возможно.

Ответы [ 3 ]

5 голосов
/ 12 октября 2011

Если вы компилируете с помощью clang, вы можете получить эту информацию.

Из блока Clang ABI spec :

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 кодирует информацию о подписи. Согласно комментарию здесь , формат - это просто стандартная строка кодирования метода target-c. Тем не менее, формат подписи не задокументирован (насколько я могу судить), поэтому я не уверен, сколько вы можете предположить, что он не сломается с обновлениями компилятора.

3 голосов
/ 11 октября 2011

Я не верю, что это действительно возможно с текущим 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);
}
1 голос
/ 09 июня 2012

Да, мы можем, поскольку я ответил в другом вопросе .

Структура блока - это опубликованный стандарт лягушек, и AFAIK можно использовать по своему усмотрению.

...