12

我想通过为 Objective-C 块创建一个类别来添加功能。

__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
4

5 回答 5

13

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

然而...

我要告诉你的内容应该严格用作学习中的练习,并且永远不要在任何形式的生产环境中使用。

  1. 一些运行时内省揭示了一些有趣的信息。有许多类包含单词“Block”。其中一些看起来很有希望:__NSStackBlock__NSMallocBlock__NSAutoBlockNSBlock
  2. 更多的自省表明,有前途的类继承自NSBlock

所以看起来任何块都将是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消息。

使用风险自负,仅用于实验。

于 2011-01-19T06:40:54.257 回答
6

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

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

    // 打印“type = __NSGlobalBlock__”
    NSLog(@"type = %@", [aBlock 类]);

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

本文本文包含一些关于块实现的有用信息。

就您最初的观点而言,为什么不NSArray为您的mapTo方法创建一个类别?对于这种功能来说,这似乎是一个更好的地方。

更新

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

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

@interface NSObject(块扩展)
- (无效)富;
@结尾

@implementation NSObject(块扩展)
-(无效)富
{
    // 不确定如何确定 self 是否是块,因为两者都不是
    // __NSGlobalBlock__ 也不是它的任何超类(除了 NSObject)
    // 编译器可以访问
    if ([[[self class] description] isEqual:@"__NSGlobalBlock__"])
    {
        NSLog(@"foo");
        // 怎么办?
        // 不能调用 self(),它不会编译
        // 我还能怎么调用这个块?
    }
}
@结尾

...

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

// 打印“foo”
[aBlock foo];
于 2011-01-19T05:52:45.370 回答
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

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

于 2011-09-25T01:08:56.080 回答
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 ”类型的实例,除非范围内没有捕获的变量,否则它将在堆栈中创建,当它被复制时,会将块移动到 HEAP 和每个参考将被保留!

于 2013-01-15T00:29:28.037 回答
-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 通常用于包装变量,而不是块本身。

于 2011-01-19T05:29:53.223 回答