在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.:/
答案 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++;
});
}