Проверка типа блока Objective-C?

это в первую очередь любопытство, я не совсем уверен, что это практическое использование, но здесь идет.

поскольку блоки также являются объектами Objective-C, можно ли проверить их тип? То есть, реагирует ли он на isKindOfClass: сообщение и как использовать это сообщение в отношении блоков?

моя наивная мысль, что это, вероятно, так:

-(void) aMethod {
    typedef int (^BlockA)(int x, int y);
    id blockVar = ...; // get a block from somewhere
    if([blockVar isKindOfClass:BlockA]) {
        BlockA blockVarA = blockVar;
        int result = blockVarA(1,2);
    }
}

код выше, вероятно, не будет работать. Но если это ... --9-- > is возможно проверить блок тип, как правильно это сделать?

5 ответов


можно сделать, вроде как.

но сначала давайте разберемся. -[NSObject isKindOfClass:] могу сказать, что это блок, и это все. Е. Г. Я верю этой строка кода -- якобы & к сожалению ПЛОХАЯ ИДЕЯ -- вернет YES для блоков на настоящем Lion & iOS 5.x:

[myBlock isKindOfClass:NSClassFromString(@"NSBlock")]

это не поможет вам отличить сигнатуру функции блока.

но это можно сделать, зацепив подпись из блока документированная внутренняя структура. Код следует за примером приложения командной строки OS X, большая часть которого вырвана изMABlockClosure (большой полная объяснение). (Обновление: проект Github CTObjectiveCRuntimeAdditions также, по-видимому, предоставляет код библиотеки именно для этой цели.)

#import <Foundation/Foundation.h>

struct BlockDescriptor {
    unsigned long reserved;
    unsigned long size;
    void *rest[1];
};

struct Block {
    void *isa;
    int flags;
    int reserved;
    void *invoke;
    struct BlockDescriptor *descriptor;
};

static const char *BlockSig(id blockObj)
{
    struct Block *block = (void *)blockObj;
    struct BlockDescriptor *descriptor = block->descriptor;

    int copyDisposeFlag = 1 << 25;
    int signatureFlag = 1 << 30;

    assert(block->flags & signatureFlag);

    int index = 0;
    if(block->flags & copyDisposeFlag)
        index += 2;

    return descriptor->rest[index];
}

int main(int argc, const char * argv[])
{
    @autoreleasepool {

        int (^block)(NSNumber *) = ^(NSNumber *num) { 
            NSLog(@"%@ %@", NSStringFromClass([num class]), num); 
            return [num intValue]; 
        };
        NSLog(@"signature %s", BlockSig(block));
        NSLog(@"retval %d", (int)block([NSNumber numberWithInt:42]));
    }
    return 0;
}

выполнить это, и вы должны получить что-то вроде:

[58003:403] signature i16@?0@8
[58003:403] __NSCFNumber 42
[58003:403] retval 42

цифры в подписи (мне сказали, что они offsets) может быть разделен для simpler i@?@.

подпись в @encode формат, который не идеален (например, большинство объектов сопоставляются с одинаковыми @), но должна позволить вам некоторые возможность различать блоки с разными сигнатурами во время выполнения.

хотя это не задокументировано в ссылке Apple, мои тестовые точки на @? будучи кодом для типа блока, который имеет смысл подписи выше. Я нашел clang-разработчиков обсуждение по этому вопросу, который, кажется, поддерживает это.


в "BlockA" в (^BlockA) - это имя переменной (в данном случае typedef), а не ее класс.
Блоки являются объектами, но не регулярными подклассами NSObject. Они реализуют только подмножество методов. -isKindOfClass: вероятно, просто сбой.
Блоки имеют тип NSMallocBlock или NSConcreteGlobalBlock, ... в зависимости от того, где они были созданы (куча, стек, ...).


кажется, что блоки имеют такие классы, как __NSGlobalBlock__, __NSStackBlock__ или __NSMallocBlock__, etc., чья цепочка наследования в конечном итоге переходит в NSBlock а то NSObject. Так ты мог бы


а также Apple, не имея ничего, что я могу сказать по этому вопросу, тыча в блок с class_copyMethodList и method_getName не показывает очевидных открытых методов. Поэтому я собираюсь сказать, что невозможно проверить их тип.


старый вопрос, но все же:

Если вы хотите простой способ сделать это: (скомпилируйте его с помощью-fno-objc-arc)

Class __NSGlobalBlock__CLASS () {
    static Class result = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        dispatch_block_t thisIsAGlobalBlock = ^{// a block with no variables will be a __NSGlobalBlock__
        };
        result = [[thisIsAGlobalBlock class] retain];
    });
    return result;
};

Class __NSStackBlock__CLASS () {
    static Class result = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        __block dispatch_block_t thisIsAStackBlock = ^{
            return ;// we really DON'T want infinate recursion
            thisIsAStackBlock();// including a reference to __block var makes this a __NSStackBlock__
        };
        result = [[thisIsAStackBlock class] retain];
    });
    return result;
};

Class __NSMallocBlock__CLASS () {
    static Class result = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        __block dispatch_block_t thisIsAMallocBlock = Block_copy(// << turns the __NSStackBlock__ Block into a __NSMallocBlock__
                                                                 ^{
            return ;// we really DON'T want infinate recursion
            thisIsAMallocBlock();// including a reference to __block var makes this a __NSStackBlock__
        });

        result = [[thisIsAMallocBlock class] retain];
        Block_release(thisIsAMallocBlock);
    });
    return result;
};

Тестовый Код:

@autoreleasepool {

    __block dispatch_block_t iAmAGlobalBlock = ^{


    };


    __block dispatch_block_t iAmAStackBlock = ^{
        return;
        iAmAStackBlock();
    };


    dispatch_block_t iAmHeapBlock = Block_copy(iAmAStackBlock);
    dispatch_block_t iAmNotAHeapBlock = Block_copy(iAmAGlobalBlock);


    if ([iAmAGlobalBlock isKindOfClass:__NSGlobalBlock__CLASS()]) {

        NSLog(@"very great success!");
    }

    if ([iAmAStackBlock isKindOfClass:__NSStackBlock__CLASS()]) {

        NSLog(@"another great success!");
    }


    if ([iAmHeapBlock isKindOfClass:__NSMallocBlock__CLASS()]) {

        NSLog(@"also great success!");
    }


    if ([iAmNotAHeapBlock isKindOfClass:__NSGlobalBlock__CLASS()]) {

        NSLog(@"yet another great success!");
    }




    NSLog (@"Block classes, as reported by NSStringFromClass():\n__NSGlobalBlock__CLASS() = %@\n__NSStackBlock__CLASS()  = %@\n__NSMallocBlock__CLASS() = %@\n[iAmAGlobalBlock class]  = %@\n[iAmAStackBlock class]   = %@\n[iAmHeapBlock class]     = %@\n[iAmNotAHeapBlock class] = %@\n",

           NSStringFromClass(__NSGlobalBlock__CLASS()),
           NSStringFromClass(__NSStackBlock__CLASS()),
           NSStringFromClass(__NSMallocBlock__CLASS()),

           NSStringFromClass([iAmAGlobalBlock class]),
           NSStringFromClass([iAmAStackBlock  class]),
           NSStringFromClass([iAmHeapBlock    class]),
           NSStringFromClass([iAmNotAHeapBlock    class])


           );



    Block_release(iAmHeapBlock);
    Block_release(iAmNotAHeapBlock);// not really needed, but since we did "Block_copy" it...

}