读取连续写入的全局变量

时间:2011-01-13 21:25:45

标签: iphone ios objective-c

在iPhone上,我们从相机拍摄图像并将其保存在全局UIImage属性中。在另一个视图控制器类中,我们试图访问此属性以显示最后写入的属性(自重写以来只有一个)。当我们尝试阅读图像时,我们会发生崩溃,说'exe bad access'。我们猜测这意味着当我们尝试访问图像时,图像不会被写入。

我们如何解决这个问题?项目的性质要求经常更新此图像,我们需要在一些用户输入之后拍摄该图像并使用它。我们需要访问它。

一个可能令人困惑的证据是,即使使用NSTimer将图像访问延迟10秒(仅用于诊断问题),我们也会遇到相同的错误。这是在停止相机会话之后,因此在此之后应该没有新的输入。


更多问题信息:

这是在每帧上从相机接收图像的功能。每8帧,我们将副本保存到全局变量并处理图像。

- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection { 

    UIImage *image = [self imageFromSampleBuffer:sampleBuffer];

    dispatch_async(dispatch_get_main_queue(), ^{
        liveImageView.image = image;
        if (mod%8==0) {
            [self analyzeImage:image];
            currentImage = image;
            NSLog(@"retain count: %d", [currentImage retainCount]);
        }
    mod++;
    });

}

var是一个全局属性,因为完全独立的类需要能够在需要最新图像时通过命令访问它。

retaincount日志主要返回2,但有时会返回3.:/

5 个答案:

答案 0 :(得分:0)

也许编写图像的代码可以设置imageIsWritten指示符(每次新写入开始时都会重置),其他类可以等到它true。然后,其他类可以设置imageIsBeingRead指示符以防止覆盖(图像编写者必须等到!imageIsBeingRead才能写入)。并且可能将整个事物包装在某种类型的控制器类中......这个技术有一个名称(我很确定),我现在不记得......

(这假设是一个简单的单线程应用程序。我不知道iPhone上有哪种类型的线程库或其他工具,所以你可能想要研究一些可能更好的更通用的解决方案)。

答案 1 :(得分:0)

通过对变量使用锁定读/写来实现此目的。您如何使用数据将取决于您如何访问它。通常可以接受:

ENTER_LOCK
UIImage* img = [[theStaticImage retain] autorelease];
EXIT_LOCK
return img;

但如果要改变数据,则必须更改它。当然,你的写作看起来像这样:

ENTER_LOCK
if (theStaticImage != theNewImage) {
  [theStaticImage autorelease];
  theStaticImage = [theNewImage retain];
}
EXIT_LOCK

如果你想改变数据,那么你需要让客户端访问锁或实现超出客户可见性的每个突变。

答案 2 :(得分:0)

如果您将UIImage全局属性声明为 atomic 而不是 nonatomic ,则该属性将始终完全设置 getter 之前将值返回给您。它比编码锁更简单 - 如果缺乏完全写入确实是问题 - 它应该解决你的问题。

编辑添加:请注意,只有当您@synthesize属性时才会这样;如果你自己编写getter和setter,那么你需要进行手动锁定。

(注意:默认情况下,将nonatomic保留在声明中会使属性atomic生效,但如果您有意添加atomic,则稍后会审核它是故意的。)

答案 3 :(得分:0)


注意:此信息已移至问题中。


更多问题信息:

这是在每帧上从相机接收图像的功能。每8帧,我们将副本保存到全局变量并处理图像。

- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection { 

    UIImage *image = [self imageFromSampleBuffer:sampleBuffer];

    dispatch_async(dispatch_get_main_queue(), ^{
        liveImageView.image = image;
        if (mod%8==0) {
            [self analyzeImage:image];
            currentImage = image;
            NSLog(@"retain count: %d", [currentImage retainCount]);
        }
    mod++;
    });

}

var是一个全局属性,因为完全独立的类需要能够在需要最新图像时通过命令访问它。

retaincount日志主要返回2,但有时会返回3.:/

答案 4 :(得分:0)

如果取消分配对象,则发送给它的任何消息的结果都是垃圾。由于块被添加到主队列,因此在autorelease pool is drained之后取消分配图像对象之后,将在事件循环的稍后迭代中调用它。您无法信任该区块内的retainCount

在当前实现中,您必须将对象保留在GCD块之外,然后在其中释放它。

-(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection { 

    UIImage *image = [[self imageFromSampleBuffer:sampleBuffer] retain];

    dispatch_async(dispatch_get_main_queue(), ^{
        liveImageView.image = image;
        if (mod%8==0) {
            [self analyzeImage:image];
            currentImage = image;
        }
        mod++;
        [image release];
    });
}

更好的方法是将其提供给适当的对象,以便所有权概念可以防止图像提前被丢弃。例如:

-(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection { 
    self.recentCapture = [self imageFromSampleBuffer:sampleBuffer];

    dispatch_async(dispatch_get_main_queue(), ^{
        liveImageView.image = self.recentCapture;
        if (mod%8==0) {
            [self analyzeImage:self.recentCapture];
            /* rather than the global 'currentImage' variable, 
               send a message to some other object informing 
               it the image is ready for processing. This will 
               introduce the image to the other object, no global 
               needed
             */
            ...
        }
        mod++;
    });
}