【问题标题】:NSZombieEnabled breaking working code?NSZombieEnabled 破坏工作代码?
【发布时间】:2011-02-28 20:06:22
【问题描述】:

我在UIImageManipulation.m中有如下方法:

+(UIImage *)scaleImage:(UIImage *)source toSize:(CGSize)size
{
    UIImage *scaledImage = nil;
    if (source != nil)
    {
        UIGraphicsBeginImageContext(size);
        [source drawInRect:CGRectMake(0, 0, size.width, size.height)];
        scaledImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
    }
    return scaledImage;
}

我在不同的视图中调用它:

imageFromFile = [UIImageManipulator scaleImage:imageFromFile toSize:imageView.frame.size];

(imageView是之前分配的UIImageView)

这在我的代码中效果很好。我完美地调整了图像的大小,并且抛出了零错误。我也没有在构建->分析下弹出任何东西。但是当我打开NSZombieEnabled 来调试另一个EXC_BAD_ACCESS 问题时,代码会中断。每一次。我可以关闭NSZombieEnabled,代码运行良好。我打开它,然后砰的一声。破碎的。我注释掉这个电话,它又可以工作了。每次,它都会在控制台中给我一个错误:-[UIImage release]: message sent to deallocated instance 0x3b1d600。如果 `NSZombieEnabled 关闭,则不会出现此错误。

有什么想法吗?

--编辑--

好吧,这可要了我的命。我到处都设置了断点,但我仍然无法抓住这个东西。这是我调用scaleImage 方法时的完整代码:

-(void)setupImageButton
{
    UIImage *imageFromFile;

    if (object.imageAttribute == nil) {
        imageFromFile = [UIImage imageNamed:@"no-image.png"];
    } else {
        imageFromFile = object.imageAttribute;
    }
    UIImage *scaledImage = [UIImageManipulator scaleImage:imageFromFile toSize:imageButton.frame.size];
    UIImage *roundedImage = [UIImageManipulator makeRoundCornerImage:scaledImage :10 :10 withBorder:YES];
    [imageButton setBackgroundImage:roundedImage forState:UIControlStateNormal];
}

另一个 UIImageManipulator 方法 (makeRoundCornerImage) 不应该导致错误,但为了以防我忽略了某些东西,我将整个文件扔到了 github here

不过,这与这种方法有关。必须。如果我将其注释掉,它会很好用。如果我把它留在里面,错误。但它不会在 NSZombieEnabled 关闭时抛出错误。

【问题讨论】:

    标签: iphone xcode exc-bad-access dealloc nszombie


    【解决方案1】:

    NSZombieEnabled 的目的是检测在对象被释放后发送给对象的消息。您看到的控制台错误是 NSZombieEnabled,告诉您 release 消息正在发送到 UIImage 的已释放实例。通常这样的错误是由于对release 的调用过多,或者对retain 的调用不足。

    在这种情况下,您的scaleImage:toSize: 方法会返回一个自动释放的UIImage。您从 NSZombieEnabled 收到的错误消息表明您可能会在返回此对象后释放它。这可以解释你的错误。当你的自动释放池耗尽时,它会尝试释放一个已经被释放的对象。

    您将imageFromFile 传递给scaleImage:toSize:,然后将相同的变量重新分配给返回值。这个习语本身并没有什么问题,但确实需要格外小心以避免像这样的内存错误。您正在覆盖对原始对象的引用,因此您必须确保它在分配之前自动释放,或者保存一个单独的引用,您可以在分配之后手动释放它。否则你的原始对象会泄漏。

    【讨论】:

    • 但是为什么这只会在设置 NSZombieEnabled 时给我一个错误?如果它正在释放一个已经释放的对象,它不应该以任何一种方式抛出错误吗?你是说最好使用UIImage *scaledImage = [ scaleImage:foo...],然后立即使用[foo release]?仍在努力解决内存管理问题。
    • 我见过向已释放对象发送消息不会导致 EXC_BAD_ACCESS 的情况。我不清楚为什么会发生这种情况,但似乎在运行时执行 dealloc 的时间与操作系统实际回收内存的时间之间可能存在一些延迟。如果 foo 的保留计数为 1(例如,如果它是使用 [[UIImage alloc] init*] 创建的),则使用两个变量 scaledImagefoo 的示例看起来是正确的。值得每周重新阅读 Cocoa 的内存管理编程指南,直到它点击为止。
    • 现在已经尝试了 2 天来追踪这个问题,但我无法弄清楚。你介意再看一下代码吗?我已经用更多信息对其进行了编辑。
    • 为了帮助缩小范围,请在调用 setBackgroundImage:forState: 时设置断点:然后在控制台中的 gdb 提示符处,执行“po scaledImage”、“po roundedImage”、“po imageFromFile”、和“po [object imageAttribute]”。它将输出每个对象的描述,包括其十六进制内存地址(例如 0x3b1d600)。然后点击继续,查看 NSZombieEnabled 给你一个错误的对象地址,以确定哪个对象在被释放后正在接收消息。
    • makeRoundCornerImage:::withBorder: 方法的一个奇怪之处在于,当 borderYES 时,它返回一个保留对象,而当 border 为 NO 时,它返回一个自动释放的对象。在这种情况下,您传递的是 YES,所以我希望这会泄漏内存,但这并不能解释为什么 NSZombieEnabled 会抱怨。
    【解决方案2】:

    错误是由于UIImageManipulatormakeRoundedCornerImage 方法中正在进行的发布。仍然不知道为什么没有打开 NSZombieEnabled 就无法接收到它,但就是这样。

    您可以在我在原始问题中发布的 Gist 中找到违规行:第 74 行。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-09-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多