【问题标题】:Executing Blocks From NSArray?从 NSArray 执行块?
【发布时间】:2011-03-16 12:51:42
【问题描述】:

我只是在想,如果我创建两个块然后将它们添加到 NSArray 中,您可以将块视为对象,有没有办法从数组中执行它们?

int (^Block_001)(void) = ^{ return 101; };
int (^Block_002)(void) = ^{ return 202; };
NSArray *array = [NSArray arrayWithObjects:Block_001, Block_002, nil];

编辑:为清晰起见根据@davedelong 的出色回答进行更新

int (^Block_001)(void) = [^{ return 101; } copy];
int (^Block_002)(void) = [^{ return 202; } copy];
NSArray *array = [NSArray arrayWithObjects:Block_001, Block_002, nil];

[Block_001 release];
[Block_002 release];

【问题讨论】:

  • 很好的一组答案....我看到了标题,并希望获得一些简单的代表点。人们得到了相当彻底的覆盖。 :)
  • 只是快速点,如果你不复制/释放块将在堆栈中......所以如果堆栈被破坏,应用程序会崩溃吗?

标签: iphone objective-c cocoa-touch nsarray objective-c-blocks


【解决方案1】:

@KennyTM 和@David 是正确的,但您的代码可能是错误的。原因如下:

当使用对象创建NSArray 时,它将retain 放入其中的对象。对于块,它使用Block_retain 函数。这意味着该数组保留了您创建的块,但存在于堆栈中(块是非常罕见的 Objective-C 对象示例之一,无需深入研究即可在堆栈上创建荒谬的伎俩)。这意味着一旦此方法退出,您的数组现在指向垃圾,因为它指向的块不再存在。要正确执行此操作,您应该这样做:

int (^Block_001)(void) = [^{ return 101; } copy];
int (^Block_002)(void) = [^{ return 202; } copy];
NSArray *array = [NSArray arrayWithObjects:Block_001, Block_002, nil];

[Block_001 release];
[Block_002 release];

通过在块上调用copy,您明确地将块从堆栈中移到堆上,在方法/函数退出后它可以安全地保留在堆上。然后,在将块添加到阵列后,您必须平衡您的 copy(由于 NARC 规则)和随后对 release 的调用。有意义吗?

【讨论】:

  • 嗨,戴夫,是的,我明白,我不知道堆栈上正在创建块(今天才刚刚开始查看块)。非常感谢。
  • 嗨 Dave,我发现了这个问题和 David Gelhar 接受的答案,所以我实现了它,它运行良好,它没有崩溃。进一步阅读,您的回答是有道理的,但没有解释为什么大卫的代码没有崩溃(立即?)。你有什么线索吗?
  • @Tieme David 的代码不会立即崩溃,因为当您执行存储在数组中的块时,您仍然处于创建块的相同词法范围内。如果你将该数组作为方法的返回值返回,然后尝试执行调用函数中的块,那么它会崩溃。虽然如果你使用 ARC 可能不会,因为编译器会为你插入必要的复制调用。
  • 好的,我去测试一下。但是,尽管编译器可能会插入副本,但您不应该像 releaseretain 那样将其遗漏,对吧?
【解决方案2】:

当然,您只需像任何其他块一样使用() 调用它,但是您需要对从NSArray 检索到的值进行类型转换。这是一个示例(添加了 typedef,否则我会头疼):

typedef int (^IntBlock)(void);
IntBlock Block_001 = ^{ return 101; };
IntBlock Block_002 = ^{ return 202; };
NSArray *array = [NSArray arrayWithObjects:Block_001, Block_002, nil];
int x = ((IntBlock)[array objectAtIndex:0]) (); // now x == 101

【讨论】:

  • 好的,所以我不熟悉函数指针,我说对了吗? typedef 定义了“IntBlock”,它是一个指向返回 int 且不带参数的块的指针。我想我明白了,并查看替代方案(我很确定我会弄错)我感谢您决定使用 typedef :) 非常感谢。
  • 你不需要投射它。 int (^block)() = [array objectAtIndex:0] 工作得很好。毕竟,指针就是指针,至少在像C及其派生词这样的低级语言中是这样。
  • @aroth 当然,您还可以使用对适当类型变量的赋值来执行从-objectAtIndex 返回的通用id 类型的“转换”。你不能做的是int y = [array objectAtIndex:0]();(编译器会抱怨“调用类型id不是函数或函数指针”)。一种方式(强制转换)或另一种方式(分配给块指针),你需要告诉编译器“我知道我从数组中拉出的这个东西是一个块,所以当我说()时,请调用它! "
【解决方案3】:

当然可以。

int (^x)(void) = [array objectAtIndex:0];
printf("%d\n", x()); // prints 101.

【讨论】:

  • 我确实考虑过这个,非常有趣。您能否为我澄清一下,我将“int(^x)(void)”称为指向块的指针是否正确?只是想确保我得到的术语是正确的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-27
  • 1970-01-01
相关资源
最近更新 更多