后台处理导致 BAD ACCESS 崩溃?
我在这里按照本指南进行后台处理:
并且唯一的1行进行后台处理的代码是:
sound = flite_text_to_wave([cleanString UTF8String], voice);
但由于某种原因,我的访问信号很差。
调试它表明它也在该行崩溃。这是我现在在该部分中的代码。请记住,其中大部分只是 sfoster 项目中的默认 Flite 内容,以前没有任何问题,当它全部在一起时,没有分成 3 个。
-(void)speakText:(NSString *)text //This is called by my app
{
cleanString = [NSMutableString stringWithString:@""];
if([text length] > 1)
{
int x = 0;
while (x < [text length])
{
unichar ch = [text characterAtIndex:x];
[cleanString appendFormat:@"%c", ch];
x++;
}
}
if(cleanString == nil)
{ // string is empty
cleanString = [NSMutableString stringWithString:@""];
}
//The next line i've put in from the link
[self performSelectorInBackground:@selector(backgroundTextToSpeech) withObject:nil];
}
-(void)backgroundTextToSpeech {
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
//The following line is the one it crashes on
sound = flite_text_to_wave([cleanString UTF8String], voice);
[self performSelectorOnMainThread:@selector(backToForegroundTextToSpeech) withObject:nil waitUntilDone:YES];
[pool release];
}
-(void)backToForegroundTextToSpeech {
NSArray *filePaths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
NSString *recordingDirectory = [filePaths objectAtIndex: 0];
// Pick a file name
tempFilePath = [NSString stringWithFormat: @"%@/%s", recordingDirectory, "temp.wav"];
// save wave to disk
char *path;
path = (char*)[tempFilePath UTF8String];
cst_wave_save_riff(sound, path);
// Play the sound back.
NSError *err;
[audioPlayer stop];
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:tempFilePath] error:&err];
[audioPlayer setDelegate:self];
[audioPlayer prepareToPlay];
}
任何想法我做错了什么/我可以采取什么不同的措施来阻止这发生了?
编辑:使用下面发布的一些代码更改调试器后的图片:
I followed this guide here to doing background processing:
And the only 1 line of code which was put to background processing was:
sound = flite_text_to_wave([cleanString UTF8String], voice);
But for some reason i got a bad access signal.
Debugging it shows that it crashes on that line too. This is the code I now have in that part. Bear in mind that most of this is just default Flite stuff from the sfoster project which has had no problems before, when it was all together, not separated into 3.
-(void)speakText:(NSString *)text //This is called by my app
{
cleanString = [NSMutableString stringWithString:@""];
if([text length] > 1)
{
int x = 0;
while (x < [text length])
{
unichar ch = [text characterAtIndex:x];
[cleanString appendFormat:@"%c", ch];
x++;
}
}
if(cleanString == nil)
{ // string is empty
cleanString = [NSMutableString stringWithString:@""];
}
//The next line i've put in from the link
[self performSelectorInBackground:@selector(backgroundTextToSpeech) withObject:nil];
}
-(void)backgroundTextToSpeech {
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
//The following line is the one it crashes on
sound = flite_text_to_wave([cleanString UTF8String], voice);
[self performSelectorOnMainThread:@selector(backToForegroundTextToSpeech) withObject:nil waitUntilDone:YES];
[pool release];
}
-(void)backToForegroundTextToSpeech {
NSArray *filePaths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
NSString *recordingDirectory = [filePaths objectAtIndex: 0];
// Pick a file name
tempFilePath = [NSString stringWithFormat: @"%@/%s", recordingDirectory, "temp.wav"];
// save wave to disk
char *path;
path = (char*)[tempFilePath UTF8String];
cst_wave_save_riff(sound, path);
// Play the sound back.
NSError *err;
[audioPlayer stop];
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:tempFilePath] error:&err];
[audioPlayer setDelegate:self];
[audioPlayer prepareToPlay];
}
Any ideas what i've done wrong/what i can do differently to stop this happening?
EDIT: A picture of the debugger after changing it round with some code posted below:
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
首先跳出来的是:
您的类需要保存
cleanString
的保留计数。通常,这是通过保留(或复制,这通常更适合 NSString 和其他具体/不可变类型)属性来完成:
好的 - 所以这不是 100% 线程安全的,但它是惯用的。
更能抵抗线程问题的变体会将字符串作为参数传递给
backgroundTextToSpeech:(NSString *)text
。然后,backgroundTextToSpeech:(NSString *)text
将创建参数text
的副本(当然,在销毁pool
之前释放该副本)。first thing that jumps out:
your class needs to hold a retain count for
cleanString
.typically, this is accomplished via a retained (or copied, which is generally preferable with NSStrings and other concrete/immutable types) property:
ok - so this isn't 100% thread-safe, but it is idiomatic.
the variant which is more resistant to threading issues would pass the string as an argument to
backgroundTextToSpeech:(NSString *)text
.backgroundTextToSpeech:(NSString *)text
would then create a copy of the argumenttext
(and of course release the copy beforepool
is destroyed).你不明白自动释放是如何工作的。您为
cleanString
变量分配一个自动释放的对象,然后在使用该值的后台开始一些处理。但是,当启动后台处理的方法将控制权返回到主运行循环时,存储在 cleanString 中的自动释放字符串将被释放,并且后台线程会陷入僵尸状态。您可以使用 Grand Central Dispatch< 来简化代码/a>:
优点是您不必维护自己的自动释放池,您不必添加额外的方法只是为了在后台执行一行并且该块将保留您的字符串,这样你就不会崩溃。
但你当然不应该在不知道发生了什么的情况下仅仅为了解决自动释放问题而使用 GCD。内存管理是基础,你必须100%确定你在做什么。
如果这超出了您的想象,请确保您了解 Cocoa 中的内存管理是如何工作的,例如通过阅读此 Objective-C斯科特·史蒂文森的教程。你必须这样做,没有其他办法。
然后回到代码,您将看到存储到 cleanString 中的可变字符串是自动释放的,这意味着它们将在您离开当前函数后不久被释放。在后台运行选择器后,退出当前函数,并且存储在
cleanString
中的字符串被释放。不久之后,后台线程到达以下行:但由于存储在 cleanString 中的对象已经被释放,因此会发生崩溃。解决方案很简单,就是保留该对象,直到使用完毕为止。您可以在运行后台线程之前保留它,并在后台线程结束时释放它。
You don’t understand how autoreleasing works. You assign the
cleanString
variable an autoreleased object and then start some processing on background that uses this value. But when the method that started the background processing returns the control to the main run loop, the autoreleased string stored in thecleanString
gets deallocated and the background thread runs into a zombie.You could simplify the code using Grand Central Dispatch:
The advantages are that you don’t have to maintain your own autorelease pool, you don’t have to add extra methods just to have one line executed in background and the block will retain your string, so that you won’t crash.
But you should certainly not use GCD just to get around the autoreleasing issue without knowing what’s happening. Memory management is the base, you must be 100% sure what you are doing.
If this is above your head, make sure you know how memory management works in Cocoa, for example by reading this Objective-C tutorial by Scott Stevenson. You have to do this, there is no way around.
Then come back to the code and you will see that the mutable strings that you store into
cleanString
are autoreleased, meaning they will get deallocated sometime soon after you leave the current function. After running the selector in background, you exit the current function and the string stored incleanString
gets deallocated. Soon after that the background thread gets to the following line:But as the object stored in
cleanString
was already deallocated, you get the crash. The solution is simply to retain the object until you are done with it. You can retain it before running the background thread and release when the background thread ends.