Objective-C块,如何保存上下文值?

Objective-C块,如何保存上下文值?,objective-c,ios,closures,objective-c-blocks,block,Objective C,Ios,Closures,Objective C Blocks,Block,这是一个棘手的问题,我无法解决 我知道Obj-C块本身不是闭包,它们的实现在某种程度上不同于Javascript闭包,但我仍将使用一个Javascript示例来展示我试图实现的目标(熟悉Javascript的人会得到它) 在Javascript上,您可以创建一个“函数工厂”,如下所示: //EXAMPLE A var _arr = [], i = 0; for(;i<8;++i) { _arr[i] = function() { console.log('Result:' +

这是一个棘手的问题,我无法解决

我知道Obj-C块本身不是闭包,它们的实现在某种程度上不同于Javascript闭包,但我仍将使用一个Javascript示例来展示我试图实现的目标(熟悉Javascript的人会得到它)

在Javascript上,您可以创建一个“函数工厂”,如下所示:

//EXAMPLE A
var _arr = [], i = 0;
for(;i<8;++i) {
  _arr[i] = function() {
    console.log('Result:' + i);
  };
}
//BY THE END OF THIS LOOP i == 7
_arr[0]();
_arr[1]();
_arr[2]();
...
_arr[7]();
…'7'在所有函数中,这是正确的,因为在对函数求值时,i的值等于8,即使在创建函数时i的值为0…7,这里我们得出结论,i是通过引用传递的,而不是通过值传递的

如果我们想“修复”这一点,并让每个函数在创建时使用i的值,我们将编写如下内容:

//EXAMPLE B
var _arr = [], i = 0;
for(;i<8;++i) {
  _arr[i] = (function(new_i){
    return function() {
      console.log(new_i);
    };
  })(i); //<--- HERE WE EVALUATE THE FUNCTION EACH TIME THE LOOP ITERATES, SO THAT EVERYTHING INSIDE OF THIS 'RETAINS' THE VALUES 'AT THAT MOMENT'
}
//BY THE END OF THIS LOOP i == 7, BUT IT DOESN'T MATTER ANYMORE
_arr[0]();
_arr[1]();
_arr[2]();
...
_arr[7]();
现在

我试图通过使用Objective-C块来做同样的事情

下面是我的代码示例A(在Obj-C中):

。。。这也是正确的,因为函数实际上包含对i的引用

问题是,我应该如何重写上面的循环以模拟示例B中所示的行为?(我保留了函数创建时的值)

我试着这样写循环:

for(;i<8;++i) {
    [_arr addObject:^(int new_i){
        return ^{
            NSLog(@"Result: %i", new_i);
        };
    }(i)];
}

for(;i如果您想返回一个块,您需要先复制它,可以发送
copy
消息,也可以使用
block\u copy
功能。为了防止内存泄漏,您必须稍后释放复制的块,例如使用
autorelease

for(;i<8;++i) {
    [_arr addObject:^(int new_i){
        return [[^{
            NSLog(@"Result: %i", new_i);
        } copy] autorelease];
    }(i)];
}

对于(;i您说Objective-C块通过引用进行捕获是不正确的。它们实际上是通过值进行捕获的。(除了
变量,我们将不在这里讨论)。您可以在这里验证:

int x = 42;
void (^foo)() = ^ { NSLog(@"%d", x); };
x = 17;
foo(); // logs "42"
您遇到的问题是,块从堆栈开始,堆栈块仅对块表达式的局部范围有效。在这种情况下,块表达式位于for循环内。这意味着在for循环结束后,块对象不再有效。但您将指向此块对象的指针放入e数组

与for循环中的局部变量一样,堆栈帧中的内存位置也会被重新使用(在本例中,这种情况会发生,但这取决于编译器)对于循环下一次迭代中的堆栈块。因此,如果检查数组中存储的值,您会发现所有对象指针都相等。因此,不是有8个指针指向8个块对象,而是有8个指针指向同一块对象。这就是为什么您认为它“通过引用”捕获它。但真正发生的是,堆栈上的块在每次迭代时都会被覆盖,因此数组包含指向此位置的指针的多个副本,因此您会一次又一次地看到同一块(上次迭代时创建的块)

答案是,您需要在将块放入数组之前复制它。复制的块位于堆上,并且具有动态生存期(与其他对象一样管理内存)

NSMutableArray*\u arr=[NSMutableArray阵列容量:0];
int i=0;

对于(;iTo澄清..如果内存管理正确,第一个版本(不将i作为参数传递给块)将打印0..7。感谢您解释“引用”部分,我从未想过它会是那样。再次感谢,非常感谢:D!
Result: 7
Result: 7
...
Result: 7
for(;i<8;++i) {
    [_arr addObject:^(int new_i){
        return ^{
            NSLog(@"Result: %i", new_i);
        };
    }(i)];
}
for(;i<8;++i) {
    [_arr addObject:^(int new_i){
        return [[^{
            NSLog(@"Result: %i", new_i);
        } copy] autorelease];
    }(i)];
}
int x = 42;
void (^foo)() = ^ { NSLog(@"%d", x); };
x = 17;
foo(); // logs "42"
NSMutableArray *_arr = [NSMutableArray arrayWithCapacity:0];
int i = 0;
for(;i<8;++i) {
    [_arr addObject:[[^{
        NSLog(@"Result: %i", i);
    } copy] autorelease]];
}
[_arr enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    ((void (^)())obj)();
}];