Фоновая обработка дает мне крах плохого доступа? - PullRequest
1 голос
/ 02 февраля 2011

Я следовал этому руководству здесь, чтобы выполнить фоновую обработку:

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

И единственная строка кода, которая была помещена в фоновую обработку, была:

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

Но по какой-то причине у меня плохой сигнал доступа.

Отладка показывает, что в этой строке происходит сбой. Это код, который я сейчас имею в этой части. Имейте в виду, что большинство из них - это просто Flite по умолчанию из проекта sfoster, у которого раньше не было проблем, когда все было вместе, а не разделено на 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];


}

Есть идеи, что я сделал неправильно / что я могу сделать по-другому, чтобы остановить это?

РЕДАКТИРОВАТЬ: изображение отладчика после его изменения с кодом, размещенным ниже:

enter image description here

Ответы [ 2 ]

1 голос
/ 02 февраля 2011

первое, что выскакивает:

вашему классу нужно хранить счет для 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

хорошо, так что это не на 100% потокобезопасно, но идиоматично.

вариант, более устойчивый к проблемам с многопоточностью, передает строку в качестве аргумента backgroundTextToSpeech:(NSString *)text. backgroundTextToSpeech:(NSString *)text создаст копию аргумента text (и, конечно, выпустит копию до уничтожения pool).

1 голос
/ 02 февраля 2011

Вы не понимаете, как работает автоматический выпуск. Вы присваиваете переменной 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];
        });
    });
}

Преимущества заключаются в том, что вам не нужно поддерживать собственный пул автоматического выпуска, вам не нужно добавлять дополнительные методы только для того, чтобы одна строка выполнялась в фоновом режиме и блок сохранит вашу строку, чтобы не разбиться.

Но вы, конечно, не должны использовать GCD только для того, чтобы обойти проблему автоматического освобождения, не зная, что происходит. Управление памятью - основа, вы должны быть на 100% уверены в том, что делаете.


Если это у вас над головой, убедитесь, что вы знаете, как работает управление памятью в Какао, например, прочитав это Objective-C учебное пособие Скотта Стивенсона . У вас есть , чтобы сделать это, пути назад нет.

Затем вернитесь к коду и увидите, что изменяемые строки, которые вы храните в cleanString, автоматически высвобождаются, то есть они будут освобождены вскоре после того, как вы покинете текущую функцию. После запуска селектора в фоновом режиме вы выходите из текущей функции, и строка, хранящаяся в cleanString, освобождается. Вскоре после этого фоновый поток попадает в следующую строку:

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

Но так как объект, хранящийся в cleanString, уже освобожден, вы получите сбой. Решение состоит в том, чтобы просто сохранить объект, пока вы не закончили с ним. Вы можете сохранить его перед запуском фонового потока и освободить после окончания фонового потока.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...