Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Checking Objective-C block type?

This is primarily a curiosity, I'm not really sure what's the practical use of this but here goes.

Since blocks are also Objective-C objects, is it possible to check their type? That is, does it respond to the isKindOfClass: message and how to use that message with respect to blocks?

My naive thought that it's probably like this:

-(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);     } } 

The code above probably won't work. But if it is possible to check a block's type, what is the correct way to do it?

like image 983
adib Avatar asked Jan 28 '12 20:01

adib


People also ask

What is __ block Objective-C?

__block is a storage qualifier that can be used in two ways: Marks that a variable lives in a storage that is shared between the lexical scope of the original variable and any blocks declared within that scope. And clang will generate a struct to represent this variable, and use this struct by reference(not by value).

How do I find the class of an object in Objective-C?

[yourObject isKindOfClass:[a class]] // Returns a Boolean value that indicates whether the receiver is an instance of // given class or an instance of any class that inherits from that class.

What does @() mean in Objective-C?

It's Shorthand writing. In Objective-C, any character , numeric or boolean literal prefixed with the '@' character will evaluate to a pointer to an NSNumber object (In this case), initialized with that value. C's type suffixes may be used to control the size of numeric literals.


2 Answers

Can do, kinda sorta.

But first, let's disambiguate. -[NSObject isKindOfClass:] can tell you it's a block, and that's about it. E.g. I believe this line of code -- ostensibly & unfortunately A BAD IDEA -- will return YES for blocks on present Lion & iOS 5.x:

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

That won't help you distinguish the block's function signature.

But it can be done, by snagging the signature from the block's documented internal struct. Code follows for an example OS X command-line app, much of which ripped from Mike Ash's MABlockClosure (great detailed explanation). (UPDATE: Github project CTObjectiveCRuntimeAdditions also apparently provides library code for just this purpose.)

#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; } 

Run this and you should get something like:

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

The numbers in the signature (I'm told they are offsets) can be stripped for simpler i@?@.

The signature is in the @encode format, which isn't perfect (e.g. most objects map to same @), but should afford you some ability to distinguish blocks with different signatures at runtime.

While it's not documented in the Apple link, my testing points to @? being the code for a block type, which makes sense of the signature above. I found a clang-developers discussion on this issue which seems to back this up.

like image 74
Clay Bridges Avatar answered Sep 22 '22 17:09

Clay Bridges


The "BlockA" in (^BlockA) is the variable name (in this case a typedef), not its class.
Blocks are objects, but not regular subclasses of NSObject. They only implement a subset of the methods. -isKindOfClass: will probably just crash.
Blocks are of the type NSMallocBlock or NSConcreteGlobalBlock, ... depending on where they were created (heap, stack, ...).

like image 38
Fabian Kreiser Avatar answered Sep 21 '22 17:09

Fabian Kreiser