debugging和发布configuration之间的不同的块行为

有问题的应用程序,简化

我的程序完美地工作。 我向你保证,我的生活,0个错误。 自豪地,我试图将应用程序打包成一个.ipa文件,用于临时分发到使用TestFlight的Betatesting。

该scheme没有工作。 应该发生的animation从未发生。 networking代码中断。 美妙地淡出音乐的button根本没有做任何事情。

事实certificate,罪魁祸首是新的和shiny的块。 当我在模拟器或我的设备上testing我的程序时,我使用了默认的“Debug”构buildconfiguration。 但是,当我将其存档以供分发(并且我相信稍后将其提交给App Store)时,XCode使用另一个“Release”configuration。 进一步调查,不同之处在于优化级别(可以在XCode的Build Settings中find它):debugging使用无(-O0),但版本使用最快,最小(-Os)。 我不知道,它是最快,最小,不起作用(tm)。 是的,这两个configuration之间的块的行为不同。

所以,我着手解决这个问题。 我已经简化了我的即将改变世界的应用程序到它的裸骨,显示在我附加到这个职位的图像。 视图控制器有一个初始值为0的实例variablesx。如果我们按b,它会产生一个线程,它会不断地检查x的值,当x变为1时,改变底部的标签。我们可以使用button来改变x的值一个。

这是我的天真代码(我使用ARC btw):

@implementation MBIViewController { int _x; } - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. _x = 0; } - (void)updateLabel { self.topLabel.text = [NSString stringWithFormat:@"x: %d", _x]; } - (IBAction)buttonAPressed:(id)sender { _x = 1; [self updateLabel]; } - (IBAction)buttonBPressed:(id)sender { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ while (_x != 1) { // keep observing for value change } dispatch_async(dispatch_get_main_queue(), ^{ self.bottomLabel.text = @"b changed me becase x changed!"; }); }); } @end 

_x是一个实例variables,所以认为块将使用指向“self”的指针而不是本地副本来访问它是合理的。 它适用于debuggingconfiguration!

但在发布版本上不起作用。 所以也许块是毕竟使用本地副本? 好的,让我们明确地使用self:

 while (self->_x != 1) { // keep observing for value change } 

在Release中不起作用。 好的,让我们直接使用指针来访问该死的variables:

 int *pointerToX = &_x; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ while (*pointerToX != 1) { // keep observing for value change } // other codes }); 

依然不起作用。 这就是当我想到智能优化编译器假定在这个multithreading的世界中没有任何可能的方式,比较的结果会改变的时候,所以它可能代替它总是TRUE或者其他的巫术。

现在,当我使用它时,事情又开始了:

 while (_x != 1) { // keep observing for value change NSLog(@"%d", _x); } 

所以,为了绕过编译器优化比较,我使出了一个getter:

 - (int)x { return _x; } 

然后使用该getter检查值:

 while (self.x != 1) { // keep observing for value change } 

它现在可以工作,因为self.x实际上是对一个函数的调用,而且编译器有足够的礼貌让这个函数能够真正地完成它的工作。 不过,我认为这是一个相当复杂的方式来做这么简单的事情。 如果你面临着“观察块内价值变化”的任务,还有没有其他的方法可以编码呢? 非常感谢!

如果你使用一个variables而不是在一个循环中修改它,那么编译器优化会导致对variables的实际访问被优化,因为你的语句可以在编译时事先被计算出来。

为了防止这种情况发生,可以使用“volatile”关键字,这可以防止编译器应用这种types的优化。

它可以与getter和setter一起工作,因为那么你需要发送一个消息给你的实例,这个实例就是一个同步点。

尝试声明_x如下:

 __block int _x; 

一般情况下,在块中使用的variables也被复制。 这将向编译器指出,如果块中修改了_x,那么更改应该在其外部可见。 它可能会解决你的问题。