2011-02-02 4 views
1

私はバックグラウンド処理を行うためにここに、このガイドに従っ:バックグラウンド処理によりBAD ACCESSのクラッシュが発生しますか?

http://evilrockhopper.com/2010/01/iphone-development-keeping-the-ui-responsive-and-a-background-thread-pattern/

とバックグラウンド処理に置かれたコードの唯一の1行だった:

音= flite_text_to_wave([cleanString UTF8Stringを]、声);

しかし何らかの理由で私は不正なアクセス信号を受け取りました。

デバッグすると、その行でもクラッシュすることが示されます。これは私が今その部分で持っているコードです。これのほとんどはちょうどそれがすべて一緒にいたとき、/ 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]; 


} 

に私が間違って何をやったか任意のアイデアを分離していない、前に何の問題もなかったsfosterプロジェクトから口論のものをデフォルトされていることに注意してくださいこの出来事をやめるために私は何ができますか?

EDIT:以下掲載いくつかのコードでそれを円形に変化した後、デバッガの絵:あなたはどのように自動解放作品を理解していない

enter image description here

答えて

1

cleanString変数に自動リリースされたオブジェクトを割り当て、この値を使用するバックグラウンドで処理を開始します。しかし、バックグラウンド処理を開始したメソッドがコントロールをメイン実行ループに戻すと、cleanStringに格納されている自動解放文字列は割り当てが解除され、バックグラウンドスレッドはゾンビになります。

あなたはGrand Central Dispatchを使用してコードを簡素化することができます:

- (void) startProcessing { 
    NSString *source = [NSString stringWithWhatever…]; 
    dispatch_queue_t targetQ = 
     dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 
    dispatch_async(targetQ, ^{ 
     sound = flite_text_to_wave…; 
     dispatch_async(dispatch_get_main_queue(), ^{ 
      [self speechProcessingDone]; 
     }); 
    }); 
} 

利点は、あなたがあなた自身の自動解放プールを維持する必要がないということです、あなただけの1行を実行したために余分なメソッドを追加する必要はありません背景にはブロックがあなたの文字列を保持するので、クラッシュすることはありません。

しかし、起こっていることを知らずに自動リリースの問題を回避するためにGCDを使用しないでください。メモリ管理が基本です。何をしているのか100%確信している必要があります。これはあなたの頭の上にある場合


、あなたはメモリ管理は、このObjective-C tutorial by Scott Stevensonを読み取ることによって、例えば、ココアでどのように動作するかを知っていることを確認してください。あなたはこれを行うにはを持って、周りはありません。

コードに戻って、cleanStringに格納されている変更可能な文字列がオートレリースされていることがわかります。つまり、現在の機能を終了してすぐに解除されます。セレクタをバックグラウンドで実行した後、現在の関数を終了し、cleanStringに格納されている文字列が割り当て解除されます。その直後、バックグラウンドスレッドは、次の行になります:

sound = flite_text_to_wave([cleanString UTF8String], voice); 

しかしcleanStringに格納されているオブジェクトがすでに割り当て解除されたとして、あなたがクラッシュを取得します。解決策は、オブジェクトを完了するまでオブジェクトを保持することです。バックグラウンドスレッドを実行する前に保持し、バックグラウンドスレッドが終了すると解放することができます。飛び出し

+0

申し訳ありませんがわかりません。私が言ったように、そのコードの大部分はfliteからインポートされていると言っていたので、私がリリースした唯一のビットはプールであり、それについては私が投稿したリンクのガイドが示したとおりでした。 – Andrew

1

最初の事:

あなたのクラスはcleanStringのための保持カウントを保持する必要があります。

典型的には、このプロパティ(NSStringsおよび他のコンクリート/不変タイプの一般的に好ましいれる、またはコピー)保持を介して達成される:

@interface MONSpeaker : NSObject 
{ 
    NSString * cleanString; 
} 

@property (copy) NSString * cleanString; 

@end 

@implementation MONSpeaker 

@synthesize cleanString; 

/* ... */ 

- (void)dealloc 
{ 
    [cleanString release], cleanString = nil; 
    [super dealloc]; 
} 

-(void)speakText:(NSString *)text // This is called by my app 
{ 

    NSMutableString * str = [NSMutableString stringWithString:@""]; 
    if([text length] > 1) 
    { 
     int x = 0; 
     while (x < [text length]) 
     { 
      unichar ch = [text characterAtIndex:x]; 
      [str appendFormat:@"%c", ch]; 
      x++; 
     } 
    } 
    if(str == nil) // why not check for nil at creation instead? 
    { // string is empty 
     str = [NSMutableString stringWithString:@""]; 
    } 
    self.cleanString = str; 
    // 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([self.cleanString UTF8String], voice); 
    [self performSelectorOnMainThread:@selector(backToForegroundTextToSpeech) withObject:nil waitUntilDone:YES]; 
    [pool release]; 
} 

@end 

OK - これは100%スレッドセーフではありませんしかし、それは慣用的です。

スレッドの問題に対してより耐性のあるバリアントは、文字列を引数としてbackgroundTextToSpeech:(NSString *)textに渡します。 backgroundTextToSpeech:(NSString *)textは、引数textのコピーを作成します(もちろん、poolが破棄される前にコピーをリリースします)。

+0

いいえ、私はこれを貼り付けましたが、もう一度クラッシュしました。 – Andrew

+2

男、あなたはウェブからコードを貼り付けて、結果がうまくいってほしいということはできません:)あなたは何をしているのかを疑う必要があります。私は私の答えを広げようとします。 – zoul

+0

私は更新した実装を読み返しました。なぜアプリケーションがクラッシュするのですか?それは更新以来、別の理由でクラッシュしていますか?私が書いた部分は1つの問題を解決しましたが、他の問題がある可能性があります。 NSZombiesを有効にして走ってみましたか? objcのメモリ管理の問題がある場合 - NSZombiesはオブジェクトを分離するのに役立ちます。 – justin

関連する問題