我想通过为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
答案 0 :(得分:13)
@pwc是正确的,因为你无法为你看不到的类创建一个类别。
...然而
__NSStackBlock
,__NSMallocBlock
,__NSAutoBlock
和NSBlock
。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
消息。
答案 1 :(得分:6)
一个块最终成为__NSGlobalBlock__
类型的实例,如下面的代码段所示:
void (^aBlock)(void) = ^(void) { NSLog(@"Hello world"); }; // prints "type = __NSGlobalBlock__" NSLog(@"type = %@", [aBlock class]);
为了创建类的类,编译器需要能够看到类的原始@interface
声明。我找不到__NSGlobalBlock__
的声明,可能是有充分理由的。
This article和this 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通常用于包装变量,而不是块本身。