线程中的UIImage未被释放/覆盖

时间:2009-11-03 13:20:02

标签: iphone multithreading uiimage cgimage

这似乎是从iPhone扫描图像的经典方法。我有一个从主线程调度的线程去扫描代码。它基本上每次创建一个新的UIImage然后删除它。

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    { 
        while (![thread isCancelled]) {
#ifdef DEBUG
            NSLog(@"Decoding Loop");
#endif
        //  [self performSelectorOnMainThread:@selector(updateImageBuffer) withObject:nil waitUntilDone:YES];           
            CGImageRef cgScreen = UIGetScreenImage();
            UIImage *uiimage = [UIImage imageWithCGImage:cgScreen];

            if (uiimage){
                CGSize size = [uiimage size];
                CGRect cropRect = CGRectMake(0.0, 80.0, size.width, 360); // Crop to centre of the screen - makes it more robust
#ifdef DEBUG
                NSLog(@"picked image size = (%f, %f)", size.width, size.height);
#endif
                [decoder decodeImage:uiimage cropRect:cropRect];
            }
            [uiimage release];
            CGImageRelease(cgScreen);
        }
    }
    [pool release];

问题是[pool release]导致ERROR_BAD_EXC(那个古老的经典)和程序炸弹。我被告知没有必要调用[uiimage release],因为我没有明确分配UIImage,但事实并非如此。如果我把这条线拿出来,内存使用会通过屋顶,程序会因为内存不足而退出。看来我不能按照我喜欢的方式进行这项工作。

有没有办法在“就地”创建UIImage?即,有一个缓冲区作为UIImage一次又一次地写入?我怀疑那会起作用吗?

更新!

尝试在主线程上执行与UIKit相关的调用,如下所示:

-(void)performDecode:(id)arg{

    // Perform the decoding in a seperate thread. This should, in theory, bounce back with a 
    // decoded or not decoded message. We can quit at the end of this thread.
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    { 
        while (![thread isCancelled]) {

#ifdef DEBUG
            NSLog(@"Decoding Loop");
#endif
            [self performSelectorOnMainThread:@selector(updateImageBuffer) withObject:nil waitUntilDone:YES];           

            if (uiimage){
                CGSize size = [uiimage size];
                CGRect cropRect = CGRectMake(0.0, 80.0, 320, 360); // Crop to centre of the screen - makes it more robust
#ifdef DEBUG
                NSLog(@"picked image size = (%f, %f)", size.width, size.height);
#endif
                [decoder decodeImage:uiimage cropRect:cropRect];
            }
        }
    }
    [pool drain];


#ifdef DEBUG
    NSLog(@"finished decoding.");
#endif


}

-(void) updateImageBuffer {
    CGImageRef cgScreen = UIGetScreenImage();
    uiimage = [UIImage imageWithCGImage:cgScreen];
    //[uiimage release];
    CGImageRelease(cgScreen);
}

当人们希望获得UIImage的“大小”时,EXC_BAD_ACCESS会变得难看

4 个答案:

答案 0 :(得分:1)

UIGetScreenImage()是私有的,没有文档,因此你不能使用它。说它没有任何关于它表明你现在拥有CGImageRef cgScreen所以你为什么要发布它?你也无法知道它是否是线程安全的,所以应该假设它不是。然后你继续发布你没有初始化,保留或复制的IImage * uiimage,所以再次 - 你不拥有它。查看docs

答案 1 :(得分:1)

正如其他人所说,你不应该释放从imageWithCGImage返回的UIImage :.它是自动释放的。当您的游泳池耗尽时,它会尝试向您已发布的图像对象发送一条释放消息,从而导致您的崩溃。

你的内存使用不断攀升的原因是你只是在循环之外排出自动释放池。您的自动释放对象会在循环内部累积。 (顺便说一下,你需要在该方法结束时释放自动释放池,因为它当前正在泄漏。)为了防止这种累积,你可以在循环内定期排空池。

但是,我建议切换到执行[[UIImage alloc] initWithCGImage:cgScreen],然后在完成后释放图像。我尽量避免在iPhone应用程序中使用自动释放的对象,以便更严格地控​​制内存使用情况和整体更好的性能。

答案 2 :(得分:0)

在这种情况下,

[uiimage release]绝对是错误的。此外,Apple强调必须在主线程上执行所有UIKit方法。其中包括UIGetScreenImage()+[UIImage imageWithCGImage:]

编辑:因此,在错误的线程上调用-[UIImage size]时会出现异常。这可能不会让你感到惊讶,因为它是不允许的。

答案 3 :(得分:0)

UIImage *uiimage = [[UIImage alloc] initWithCGImage: cgScreen];

明确说明我知道何时发布该对象似乎最有效。虚拟内存仍在增加,但物理现在保持不变。感谢您指出UIKit线程安全问题。这是我错过的一点,但似乎不会影响此时的跑步。

另外,我应该指出,Red Laser和Quickmark都使用这种扫描相机信息的方法;)