Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to create a category of the "Block" object in Objective-C

I would like to add functions by creating a category for Objective-C Blocks.

__block int (^aBlock)(int) = ^int( int n ){
    if( n <= 1 ) return n;
    return aBlock( n - 1 ) + aBlock( n - 2 );
};

Instead of just allowing the normal [aBlock copy], [aBlock retain], [aBlock release], [aBlock autorelease]. I could do thing like:

[aBlock mapTo:anArray];

Possible Category

@interface UnknownBlockClass (map)

- (NSArray *)mapTo:(NSArray *)array_;

@end
like image 267
Kendall Hopkins Avatar asked Jan 19 '11 05:01

Kendall Hopkins


People also ask

What is true about blocks in Objective-C?

Blocks are a language-level feature added to C, Objective-C and C++, which allow you to create distinct segments of code that can be passed around to methods or functions as if they were values. Blocks are Objective-C objects, which means they can be added to collections like NSArray or NSDictionary .

What does __ block do?

The __block Storage Type __block variables live in storage that is shared between the lexical scope of the variable and all blocks and block copies declared or created within the variable's lexical scope.

Why do you generally create a weak reference when using self in a block?

because block are mean to be executed at a later time, so it need to keep strong reference to all the object it access. Block can be executed MANY TIMES, so IT WON'T RELEASE self AFTER this ran. When you nil out the block, it will be dealloc, hence it will decrease the reference count to all the objects it access.


2 Answers

@pwc is correct in that you can't create a category for a class that you can't see.

However...

WHAT I AM ABOUT TO TELL YOU SHOULD BE USED STRICTLY AS AN EXERCISE IN LEARNING, AND NEVER IN ANY SORT OF PRODUCTION SETTING.

  1. Some runtime introspection reveals some interesting information. There are a number of classes that contain the word "Block". Some of them look promising: __NSStackBlock, __NSMallocBlock, __NSAutoBlock, and NSBlock.
  2. Some more introspection shows that the promising classes inherit from NSBlock

So it looks like any block is going to be some instance or subclass of NSBlock.

You can create a method on an object, like so:

@implementation Foo
- (void) doFoo {
  //do something awesome with self, a block
  //however, you can't do "self()".  
  //You'll have to cast it to a block-type variable and use that
}
@end

Then at runtime, you can move that method to the NSBlock class:

Method m = class_getInstanceMethod([Foo class], @selector(doFoo));
IMP doFoo = method_getImplementation(m);
const char *type = method_getTypeEncoding(m);
Class nsblock = NSClassFromString(@"NSBlock");
class_addMethod(nsblock, @selector(doFoo), doFoo, type);

After this, blocks should respond to the doFoo message.

USE AT YOUR OWN RISK, AND ONLY FOR EXPERIMENTING.

like image 141
Dave DeLong Avatar answered Sep 28 '22 18:09

Dave DeLong


A block winds up being an instance of type __NSGlobalBlock__, as seen in the following snippet:

    void (^aBlock)(void) = ^(void) {
        NSLog(@"Hello world");
    };

    // prints "type = __NSGlobalBlock__"
    NSLog(@"type = %@", [aBlock class]);

In order to create a category of a class, the compiler needs to be able to see the original @interface declaration of the class. I can't find the declaration for __NSGlobalBlock__ and probably for good reason.

This article and this article contain some useful information about the implementation of blocks.

To your original point, why not just make a category of NSArray for your mapTo method? It seems like a better place for that sort of functionality.

Updated

Let's say you can add a category to the Block object. How would you invoke the block from the category's method? To the best of my understanding, the only way to invoke a block is via the () operator (e.g., aBlock()). I don't think there's a way to tell from the Block object the number and types of parameters. So, what arguments would you pass in to the block invocation?

I'm not recommending you do this, but the following works...

@interface NSObject (BlockExtension)
- (void)foo;
@end

@implementation NSObject (BlockExtension)
- (void)foo
{
    // not sure how else to determine if self is a Block since neither
    // __NSGlobalBlock__ nor any of its superclasses (except NSObject) 
    // are accessible to the compiler
    if ([[[self class] description] isEqual:@"__NSGlobalBlock__"])
    {
        NSLog(@"foo");
        // now what?
        // can't call self(), it doesn't compile
        // how else can I invoke this block?
    }
}
@end

...

void (^aBlock)(void) = ^(void) {
    NSLog(@"Hello world");
};

// prints "foo"
[aBlock foo];
like image 27
pwc Avatar answered Sep 28 '22 17:09

pwc