是否可以在Objective-C中创建“块”对象的类别

时间:2011-01-19 05:07:16

标签: objective-c cocoa objective-c-blocks

我想通过为Objective-C Blocks创建一个类别来添加函数。

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

而不仅仅是允许正常的[aBlock copy][aBlock retain][aBlock release][aBlock autorelease]。我可以这样做:

[aBlock mapTo:anArray];

可能的类别

@interface UnknownBlockClass (map)

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

@end

5 个答案:

答案 0 :(得分:13)

@pwc是正确的,因为你无法为你看不到的类创建一个类别。

...然而

我要告诉你的是你应该学习严格的学习,并且从不在任何生产设置中。

  1. 一些运行时内省揭示了一些有趣的信息。有许多类包含单词“Block”。其中一些看起来很有前途:__NSStackBlock__NSMallocBlock__NSAutoBlockNSBlock
  2. 更多的内省表明,有希望的类继承自NSBlock
  3. 所以看起来任何块都是NSBlock的某个实例或子类。

    您可以在对象上创建方法,如下所示:

    @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
    

    然后在运行时,您可以将该方法移动到NSBlock类:

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

    此后,块应响应doFoo消息。

    自行承担使用风险,仅用于实验。

答案 1 :(得分:6)

一个块最终成为__NSGlobalBlock__类型的实例,如下面的代码段所示:

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

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

为了创建类的类,编译器需要能够看到类的原始@interface声明。我找不到__NSGlobalBlock__的声明,可能是有充分理由的。

This articlethis article包含有关块实现的一些有用信息。

原来,为什么不为NSArray方法制作mapTo类别?对于那种功能来说,这似乎是一个更好的地方。

更新

假设您可以向Block对象添加类别。你会如何从类别的方法中调用块?据我所知,调用块的唯一方法是通过()运算符(例如aBlock())。我不认为有一种方法可以从Block对象中分辨出参数的数量和类型。那么,你将什么参数传递给块调用?

我不建议你这样做,但以下工作......

@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];

答案 2 :(得分:1)

Dave DeLong是对的,你不能在你看不到的类上添加一个类别,但是作为块是NSBlock的子类添加:

@interface NSBlock : NSObject
@end

现在你可以“看到”NSBlock并在其上添加一个类别,例如:

@interface NSBlock (map)
- (NSArray *)mapTo:(NSArray *)array;
@end

@implementation NSBlock (map)
- (NSArray *)mapTo:(NSArray *)array
{
    ...
}
@end

在生产中实际使用的代码中,可能不是最好的事情......

答案 3 :(得分:0)

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

int i = 0;
id o = [class self];

void (^aBlock)(void) = ^(void) {

    [o setValue:0];

    NSLog(@"Hello world %d", i);
};

// prints "type = __NSGlobalBlock__" 

// Now it prints __NSStackBlock__ 
// and when moved into HEAP prints __NSMallocBlock__

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

除非在作用域中没有捕获的变量,否则一个块最终会成为“ NSGlobalBlock ”类型的实例,这是唯一可行的,否则它将在STACK中创建并在何时生成被复制,将块移动到HEAP,每个参考将被保留!

答案 4 :(得分:-2)

简单的答案是否定的。 __block变量是C级对象而不是Objective C对象。您可以调用[aBlock copy]但这会调用C函数block_copy()而不是nsobject复制方法。所以__block类型是C类型,因此您无法添加类别。

更正:__ block是C编译器中的标识符,而不是typedef。

我不确定这是否会实现您的想象,事实上我甚至不确定它的作用:

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

__block标识符告诉编译器该变量在引用块中应该是可变的,并且如果将任何引用块复制到堆中,则应该保留该变量。令我困惑的是你的代码是__block通常用于包装变量,而不是块本身。