Cocoa 块和堆栈
根据: 2) 块是在堆栈上创建的。小心点 考虑:Cocoa 块和堆栈,cocoa,objective-c-blocks,Cocoa,Objective C Blocks,根据: 2) 块是在堆栈上创建的。小心点 考虑: typedef int(^Blocky)(void); Blocky b[3]; for (int i=0; i<3; i++) b[i] = ^{ return i;}; for (int i=0; i<3; i++) printf("b %d\n", b[i]()); 但bbum似乎在说,当for循环的每个循环完成时,块从堆栈中弹出;然后,在最后一次弹出之后,第三个块恰好位于无人认领的内存中。然后,当你调用块时
typedef int(^Blocky)(void);
Blocky b[3];
for (int i=0; i<3; i++)
b[i] = ^{ return i;};
for (int i=0; i<3; i++)
printf("b %d\n", b[i]());
但bbum似乎在说,当for循环的每个循环完成时,块从堆栈中弹出;然后,在最后一次弹出之后,第三个块恰好位于无人认领的内存中。然后,当你调用块时,指针都指向第三个块???是的,这是有道理的,但你真的需要考虑一下。当b[0]被赋予其值时,“^{return 0;}”将不再使用。b[0]只是它的地址。编译器一直在覆盖堆栈上的临时函数,因此“2”只是在该空间中编写的最后一个函数。如果在创建时打印这3个地址,我打赌它们都是一样的 另一方面,如果展开赋值循环,并向“^{return 0;}”添加其他引用,比如将其赋值给c[0],则可能会看到b[0]!=b[1]!=b[2]:
b[0] = ^{ return 0;};
b[1] = ^{ return 1;};
b[2] = ^{ return 2;};
c[0] = ^{ return 0;};
c[1] = ^{ return 1;};
c[2] = ^{ return 2;};
优化设置可能会影响结果。
顺便说一句,我认为bbum并不是说pop发生在for循环完成之后——它发生在每次迭代到达结束括号(范围结束)之后
块对象[在堆栈上分配]仅在其生命周期内有效
封闭范围
在bbum的示例中,块的范围是循环的封闭大括号(bbum省略了该括号):
这看起来像是堆栈分配。每个指针都指向相同的内存区域,因为在从堆栈中弹出一个块之后,该内存区域将被重新用于下一个块
然后看起来,当调用第一个块时,恰好得到了正确的结果,但是当调用第二个块时,系统已经覆盖了回收的内存,导致了垃圾值?我仍然不清楚调用一个不存在的块如何产生一个值???你完全误解了“在堆栈上”的含义 没有所谓的“变量堆栈”。“堆栈”是指“调用堆栈”,即调用帧的堆栈。每个调用帧存储该函数调用的局部变量的当前状态。示例中的所有代码都在单个函数中,因此这里只有一个相关的调用框架。调用帧的“堆栈”不相关 提到“stack”只意味着块是在调用帧内分配的,就像局部变量一样。“在堆栈上”意味着它的生存期类似于局部变量,即具有“自动存储持续时间”,并且它的生存期限定在声明它的范围内 这意味着在创建该块的for循环的迭代结束后,该块无效。现在,指向块的指针指向一个无效对象,取消引用指针是未定义的行为。由于块的生命周期已经结束,并且它所使用的空间未被使用,因此编译器可以在调用帧中自由地将该位置用于以后的其他内容 幸运的是,编译器决定将稍后的块放在同一位置,这样当您尝试以块的形式访问该位置时,它会产生有意义的结果。但这实际上只是未定义的行为。如果编译器愿意,它可以在该空间的一部分中放置一个整数,在另一部分中放置另一个变量,也可以在该空间的另一部分中放置一个块,这样当您试图以块的形式访问该位置时,它将做各种各样的坏事,甚至可能崩溃 块的生存期完全类似于在同一范围内声明的局部变量。在一个更简单的示例中,您可以看到相同的结果,该示例使用一个局部变量来再现正在发生的事情:
int *b[3];
for (int i=0; i<3; i++) {
int j = i;
b[i] = &j;
}
for (int i=0; i<3; i++)
printf("b %d\n", *b[i]);
在这里,与块的情况一样,您还存储了一个指向循环迭代内部范围内的对象的指针,并在循环结束后使用它。同样,因为幸运,该变量的空间恰好被分配给了循环后期迭代中的同一个变量,因此它似乎给出了一个有意义的结果,即使它只是未定义的行为
现在,如果您使用的是ARC,您可能看不到引用的文本所说的发生了什么,因为ARC要求在块指针类型的变量中存储某些内容时(并且
b[i]
具有块指针类型),会生成一个副本而不是retain,并存储副本。复制堆栈块时,它将被移动到堆中(即,它是动态分配的,具有动态生存期,并且与其他对象一样受内存管理),并返回指向堆块的指针。您可以在范围之后安全地使用它。如果您在创建这3个地址时打印它们,我打赌它们都是相同的。
不。完全不同。如果它们都一样,我的下一个问题是:为什么堆栈会覆盖任何内容?这不是我对堆栈如何工作的理解。操作系统将内容推送到堆栈上,然后将其弹出——它不会覆盖堆栈上尚未弹出的内容。事实上,我的地址结果并不重要——因为bbum的代码输出0 1 2——所以在bbum写那篇文章到现在这段时间内发生了一些变化。不过,我想回答的问题是,“在堆栈上分配块”如何解释bbum得到的结果。据我所知,堆栈不会覆盖堆栈上已经存在的项,而是将新项推送到堆栈上。我不认为bbum是说弹出发生在for循环完成之后——它发生在赋值之后。
为什么赋值会导致某些内容从堆栈中弹出?@7stud,不是字面上的赋值。但如前所述,后面紧跟着右大括号(范围结束)。对不起,我应该写得更好。我将进行编辑。该块正在通过ARC从堆栈中移出,因为您正在从方法返回它,以确保
for (int i=0; i<3; i++) {#<------
b[i] = ^{ return i;};
}#<-----
typedef int(^Blocky)(void); #******TYPEDEF HERE********
@interface AppDelegate : NSObject <NSApplicationDelegate>
@end
#import "AppDelegate.h"
@interface AppDelegate ()
-(Blocky)blockTest:(int)i {
Blocky myBlock = ^{return i;}; #If the block is allocated on the stack, it should be popped off the stack at the end of this method.
NSLog(@"%p", myBlock);
return myBlock;
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
Blocky b[3];
for (int i=0; i < 3; ++i) {
b[i] = [self blockTest:i];
}
for (int j=0; j < 3; ++j) {
NSLog(@"%d", b[j]() );
}
}
@end
--output:--
0x608000051820
0x608000051850
0x6080000517c0
0
1
2
0x7fff5fbfe658
0x7fff5fbfe658
0x7fff5fbfe658
2
1606411952
1606411952
int *b[3];
for (int i=0; i<3; i++) {
int j = i;
b[i] = &j;
}
for (int i=0; i<3; i++)
printf("b %d\n", *b[i]);
b 2
b 2
b 2