调试和发布配置之间的不同块行为 [英] Different block behavior between debug and release configuration

查看:75
本文介绍了调试和发布配置之间的不同块行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的程序运行完美.我向您保证,有0个bug.荣幸地,我尝试将应用程序打包为.ipa文件,以便使用TestFlight临时分发给我的Beta测试人员.

My program works perfectly. I assure you with my life, 0 bugs. Proudly, I tried to package the application as an .ipa file for ad-hoc distribution to my beta tester using TestFlight.

该程序无效.本来应该发生的动画从未发生过.网络代码中断.漂亮地淡出音乐的按钮根本没有任何作用.

The program didn't work. Animations which are supposed to happen never happened. Network code breaks. The button to fade out the music beautifully didn't do anything at all.

事实证明,罪魁祸首是新的闪亮块.当我在模拟器或设备上测试程序时,我使用了默认的调试"构建配置.但是,当我将其存档以进行分发(并且以后相信会提交给App Store)时,XCode使用了另一个配置,即发布".进一步调查,差异是由于优化级别(您可以在XCode的Build Settings中找到它):Debug使用None(-O0),但Release使用最快,最小(-Os).我几乎不知道这是最快,最小和不起作用的(tm).是的,在这两种配置之间,块的行为有所不同.

It turns out that the culprit is the new and shiny blocks. When I test my program in the Simulator or on my device, I used the default "Debug" build configuration. But when I archive it for distribution (and I believe later for submission to the App Store), XCode uses another configuration which is "Release". Investigating further, the difference is due to the optimization level (you can find it on XCode's Build Settings): Debug uses None (-O0) but Release uses Fastest, Smallest (-Os). Little did I know, that it's Fastest, Smallest, and Doesn't Work (tm). Yes, blocks behave differently between those 2 configurations.

所以,我着手解决这个问题.我已将我即将改变的应用程序简化为裸露的骨骼,如我在本文中所附的图像所示.视图控制器有一个实例变量x,其初始值为0.如果按b,它将生成一个线程,该线程将连续检查x的值,并在x变为1时更改底部标签.我们可以使用button更改x的值.

So, I set out to solve the problem. I've simplified my soon-to-change-the-world app into its bare bones, shown in the image I've attached to this post. The view controller has an instance variable x with initial value 0. If we press b, it will spawn a thread that will continuously check the value of x, changing the bottom label when x becomes 1. We can change the value of x using button a.

这是我的幼稚代码(我正在使用ARC btw):

Here is my naive code (I'm using 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是一个实例变量,因此可以合理地认为该块将使用指向"self"的指针而不是本地副本来访问它.它适用于调试配置!

_x is an instance variable, so it is reasonable to think that the block will access it using a pointer to "self", not on a local copy. And it works on the debug configuration!

但是它不适用于Release版本.那么,也许该块毕竟使用的是本地副本?好的,让我们显式地使用self:

But it doesn't work on Release build. So perhaps the block is using a local copy after all? OK, so let's explicitly use self:

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

在Release中也不起作用.好的,让我们直接使用指针访问该死的变量:

Doesn't work either in Release. OK, so let's access the damn variable directly using pointer:

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

仍然无效.就在这时,我意识到智能优化编译器假定在多线程世界中,比较的结果将不可能改变,因此也许将其替换为始终为TRUE或其他伏都教.

Still doesn't work. This is when it dawned to me that the smart optimizing compiler assumes that there is no possible way in this multithreaded world that the result of the comparison will change, so perhaps it substituted it to be always TRUE or some other voodoo.

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

Now, when I use this, things start working again:

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

因此,为了绕过编译器优化比较,我求助于吸气剂:

So, to bypass the compiler optimizing out the comparison, I resorted to making a getter:

- (int)x
{
    return _x;
}

然后使用该吸气剂检查值:

And then checking the value using that getter:

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

它现在可以工作,因为self.x实际上是对函数的调用,并且编译器足够礼貌,可以让函数实际完成其工作.但是,我认为这是做起来如此简单的事情的一种相当复杂的方法.如果您面临着观察块内值的变化"的任务,还有其他方法可以使用它编码,是否会使用另一种模式?非常感谢!

It now works, because self.x is actually a call to a function and the compiler is polite enough to let the function actually do its job. However, I think this is a rather convoluted way to do something so simple. Is there any other way you would have coded it, another pattern that you will use, if you are faced with the task of "observing for change of value inside a block"? Thanks a lot!

推荐答案

如果您使用变量而不在循环中对其进行修改,则编译器优化会导致对变量的实际访问被优化,因为您的语句可以在编译时预先评估.

If you use a variable and do not modify it in a loop, the compiler optimization can cause the actual access to the variable to be optimized out, because your statement can be evaluated beforehand at compile time.

为防止这种情况,可以使用"volatile"关键字,该关键字可防止编译器应用这种类型的优化.

In order to prevent this, you can use the "volatile" keyword, which prevents the compiler from applying this type of optimization.

它确实可以与getter和setter一起使用,因为然后您需要向实例发送一条消息,该消息用作同步点.

It does work with getters and setters, because then you need to send a message to your instance, which serves as a synchronization point.

这篇关于调试和发布配置之间的不同块行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆