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

时间:2023-01-15 14:45:01

I would like to add functions by creating a category for 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 );
};

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

而不是仅允许正常[aBlock复制],[aBlock保留],[aBlock发布],[aBlock autorelease]。我可以这样做:

[aBlock mapTo:anArray];

Possible Category

可能的类别

@interface UnknownBlockClass (map)

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

@end

5 个解决方案

#1


13  

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

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

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. 一些运行时内省揭示了一些有趣的信息。有许多类包含单词“Block”。其中一些看起来很有希望:__ NSStackBlock,__ NSMallocBlock,__ NSAutoBlock和NSBlock。
  3. Some more introspection shows that the promising classes inherit from NSBlock
  4. 更多的内省表明,有希望的类继承自NSBlock

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

因此看起来任何块都将成为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:

然后在运行时,您可以将该方法移动到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);

After this, blocks should respond to the doFoo message.

在此之后,块应该响应doFoo消息。

USE AT YOUR OWN RISK, AND ONLY FOR EXPERIMENTING.

#2


6  

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

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

    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.

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

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.

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

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?

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

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

#3


1  

Dave DeLong is right, you cannot add a category on a class that you cannot see, but as blocks are subclasses of NSBlock adding:

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

@interface NSBlock : NSObject
@end

Now you can 'see' NSBlock and add a category on it, e.g.:

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

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

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

Still probably not the best thing to do in code that is actually used in production...

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

#4


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

It is only OKAY to say that a block winds up being an instance of type "NSGlobalBlock" unless there are no captured variables in the scope, otherwise it will be created in the STACK and when it is copied that will move the block into HEAP and every reference will be retained!

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

#5


-2  

The simple answer is no. A __block variable is a C level object not an Objective C object. You can call [aBlock copy] but this invokes a C function block_copy() not the nsobject copy method. So the __block type is a C type and therefore you can't add categories.

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

correction:__block is an identifier in the C compiler not a typedef.

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

I'm not sure if this will achieve what you think it will, infact I'm not even quite sure what it does:

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

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

the __block identifier tells the complier that the variable should be mutable in referencing blocks and should be preserved if any referencing block is copied to the heap. what confuses me about your code is that __block is usually used to wrap a variable, not a block itself.

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

#1


13  

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

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

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. 一些运行时内省揭示了一些有趣的信息。有许多类包含单词“Block”。其中一些看起来很有希望:__ NSStackBlock,__ NSMallocBlock,__ NSAutoBlock和NSBlock。
  3. Some more introspection shows that the promising classes inherit from NSBlock
  4. 更多的内省表明,有希望的类继承自NSBlock

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

因此看起来任何块都将成为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:

然后在运行时,您可以将该方法移动到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);

After this, blocks should respond to the doFoo message.

在此之后,块应该响应doFoo消息。

USE AT YOUR OWN RISK, AND ONLY FOR EXPERIMENTING.

#2


6  

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

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

    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.

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

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.

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

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?

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

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

#3


1  

Dave DeLong is right, you cannot add a category on a class that you cannot see, but as blocks are subclasses of NSBlock adding:

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

@interface NSBlock : NSObject
@end

Now you can 'see' NSBlock and add a category on it, e.g.:

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

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

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

Still probably not the best thing to do in code that is actually used in production...

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

#4


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

It is only OKAY to say that a block winds up being an instance of type "NSGlobalBlock" unless there are no captured variables in the scope, otherwise it will be created in the STACK and when it is copied that will move the block into HEAP and every reference will be retained!

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

#5


-2  

The simple answer is no. A __block variable is a C level object not an Objective C object. You can call [aBlock copy] but this invokes a C function block_copy() not the nsobject copy method. So the __block type is a C type and therefore you can't add categories.

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

correction:__block is an identifier in the C compiler not a typedef.

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

I'm not sure if this will achieve what you think it will, infact I'm not even quite sure what it does:

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

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

the __block identifier tells the complier that the variable should be mutable in referencing blocks and should be preserved if any referencing block is copied to the heap. what confuses me about your code is that __block is usually used to wrap a variable, not a block itself.

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