Проверка типа блока 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);
    }
}

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

27
задан adib 28 January 2012 в 20:24
поделиться

3 ответа

Можно сделать , своего рода Сорта.

Но сначала давайте устраним неоднозначность. -[NSObject isKindOfClass:] может сказать вам, что это блок, и это все. Например. Я считаю эту строку кода - якобы & amp; к сожалению, A BAD IDEA - вернет YES для блоков на нынешнем Lion & amp; 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

Числа в подписи (мне говорят, что они являются смещениями) можно упростить для упрощения i@?@.

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

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

42
ответ дан 28 November 2019 в 04:39
поделиться

Похоже, что блоки имеют классы типа __NSGlobalBlock__, __NSStackBlock__ или __NSMallocBlock__ и т. Д., Цепочка наследования которых в конечном итоге переходит к NSBlock, а затем NSObject. Таким образом, вы можете проверить, является ли что-то блоком, выполнив [... isKindOfClass:NSClassFromString(@"NSBlock")]. Однако, кажется, нет никакого способа запросить сигнатуру блока (тип возвращаемого значения и типы аргумента) во время выполнения, поэтому вы не сможете различить блоки с разными сигнатурами.

8
ответ дан 28 November 2019 в 04:39
поделиться

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

Если вы хотите простой способ сделать это: (скомпилируйте его с -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...

}
1
ответ дан 28 November 2019 в 04:39
поделиться