alm*_*now 5 closures block objective-c ios objective-c-blocks
这是一个棘手的问题,我只是无法打击.
我知道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]();
Run Code Online (Sandbox Code Playgroud)
其中使用相应的函数填充名为_arr的数组,然后计算所有这些数组.请注意,上面代码的结果将输出...
Result: 7
Result: 7
Result: 7
...
Result: 7
Run Code Online (Sandbox Code Playgroud)
...'7'在所有函数中都是正确的,因为在函数得到评估的时候,i的值等于8,即使i的值在创建它们时为0 ... 7,这里我们得出结论,我是通过引用而不是通过值传递的.
如果我们想要"修复"这个并且让每个函数在创建时使用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]();
Run Code Online (Sandbox Code Playgroud)
而不是直接创建最终函数,而是使用一个中间闭包,它返回最终函数,其中包含正确的值'fixed'; 因此将返回:
Result: 0
Result: 1
Result: 2
...
Result: 7
Run Code Online (Sandbox Code Playgroud)
现在...
我正在尝试使用Objective-C块做同样的事情.
这是我的示例A的代码(在Obj-C中):
NSMutableArray *_arr = [NSMutableArray arrayWithCapacity:0];
int i = 0;
for(;i<8;++i) {
[_arr addObject:^{
NSLog(@"Result: %i", i);
}];
}
//BY THE END OF THIS LOOP i == 7
[_arr enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
((void (^)())obj)();
}];
Run Code Online (Sandbox Code Playgroud)
这将输出......
Result: 7
Result: 7
...
Result: 7
Run Code Online (Sandbox Code Playgroud)
...这也是正确的,因为该函数实际上包含对i的引用.
问题是,我应该如何重写上面的循环以模拟示例B中显示的行为?(我保留了函数创建时的价值)
我试过写这样的循环:
for(;i<8;++i) {
[_arr addObject:^(int new_i){
return ^{
NSLog(@"Result: %i", new_i);
};
}(i)];
}
Run Code Online (Sandbox Code Playgroud)
但它在编译时会出现以下错误:返回生成在本地堆栈上的块
谢谢,最好; D!
new*_*cct 11
你说Objective-C阻止通过引用捕获是不正确的.它们实际上是按价值捕获的 (除了__block变量,我们不会在这里讨论.)你可以在这里验证:
int x = 42;
void (^foo)() = ^ { NSLog(@"%d", x); };
x = 17;
foo(); // logs "42"
Run Code Online (Sandbox Code Playgroud)
您遇到的问题是块从堆栈开始,堆栈块仅对块表达式的本地范围有效.在这种情况下,块表达式位于for循环中.这意味着在for循环的迭代结束后,块对象不再有效.但是你将一个指向这个块对象的指针放入数组中.
与for循环内部的局部变量一样,堆栈帧中的内存位置然后在循环的下一次迭代中重新使用(在这种情况下,但是由编译器决定)堆栈块.因此,如果检查存储在数组中的值,您会发现所有对象指针都是相同的.因此,不是有8个指向8个块对象的指针,而是指向同一个块对象的8个指针.这就是为什么你认为它是"通过引用"捕获它.但真正发生的是堆栈上的块在每次迭代时都会被覆盖,所以你的数组包含指向这个位置的指针的多个副本,所以你会看到同一个块(在最后一次迭代中创建的块)一遍又一遍再次.
答案是您需要在将块放入阵列之前复制块.复制的块位于堆上并具有动态生存期(内存管理与其他对象一样).
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)();
}];
Run Code Online (Sandbox Code Playgroud)
您不需要像在JavaScript中那样使用Objective-C中的方式包装立即执行的第二个闭包.
| 归档时间: |
|
| 查看次数: |
1842 次 |
| 最近记录: |