Что вызывает SIGSEGV с использованием блоков?

#objective-c #objective-c-blocks

#objective-c #objective-c-блоки

Вопрос:

У меня есть следующий код. Я иногда получаю SIGSEGV. У меня такое чувство, что я что-то упускаю в отношении управления памятью с использованием блоков. Безопасно ли передавать replacedUrls, который автоматически отправляется в этот блок? Как насчет изменения переменной экземпляра FormattedText?

     NSMutableSet* replacedUrls = [[[NSMutableSet alloc] init] autorelease];

    NSError *error = nil; 
    NSDataDetector *detector = [NSDataDetector dataDetectorWithTypes:
                                (NSTextCheckingTypeLink | NSTextCheckingTypePhoneNumber)
                                                               error:amp;error];
    if (error) {
        return;
    }

    [detector enumerateMatchesInString:self.formattedText 
              options:0 
              range:NSMakeRange(0, [self.formattedText length]) 
              usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {

            @try  {
                if (result.resultType == NSTextCheckingTypePhoneNumber) {

                    if (!result.phoneNumber) {
                        // not sure if this is possible
                        return;
                    }

                    self.formattedText = [self.formattedText stringByReplacingOccurrencesOfString:result.phoneNumber
                                                                                       withString:[NSString stringWithFormat:@"<a href="tel://%@">%@</a>", result.phoneNumber, result.phoneNumber]];
                }
                else if (result.resultType == NSTextCheckingTypeLink) {

                    if (!result.URL) {
                        // not sure if this is possible
                        return;
                    }

                    NSString* fullUrl = [result.URL absoluteString];

                    if (!fullUrl) {
                        return; 
                    }

                    if ([replacedUrls containsObject:fullUrl]) {
                        return; 
                    }

                    // not sure if this is possible
                    if ([result.URL host] amp;amp; [result.URL path]) {
                        NSString* urlWithNoScheme = [NSString stringWithFormat:@"%@%@", [result.URL host], [result.URL path]];

                        // replace all http://www.google.com to www.google.com
                        self.formattedText = [self.formattedText stringByReplacingOccurrencesOfString:fullUrl
                                                                                           withString:urlWithNoScheme];

                        // replace all www.google.com with http://www.google.com
                        NSString* replaceText = [NSString stringWithFormat:@"<a href="%@">%@</a>", fullUrl, fullUrl];
                        self.formattedText = [self.formattedText stringByReplacingOccurrencesOfString:urlWithNoScheme
                                                                                           withString:replaceText];

                        [replacedUrls addObject:fullUrl];
                    }
                }
            }
            @catch (NSException* ignore) {
                // ignore any issues
            }
        }];
  

Комментарии:

1. После некоторого чтения я определенно вижу, где я буду создавать цикл сохранения, поскольку self будет сохранен. Тем не менее, я все еще не уверен, как это создаст реальную проблему.

2. самая хитрая строка — ‘if(error){return;} но я не уверен, как это вызывает вашу проблему (ваш код исправляется корректно, если вы вернетесь в этот момент, верно?). Сохранение self не обязательно вызывает цикл сохранения, а цикл сохранения не вызовет SIGSEGV;

3. Да, эта строка странная. Я даже не уверен, что это необходимо. Я думаю, что добавил это в качестве проверки работоспособности. Он получал SIGSEGV до того, как мы добавили эту строку. Большинство из этих операторов if были добавлены, потому что отчет о сбое просто указывал на общий блок, а не на конкретную строку. Немного раздражает, чтобы отследить проблему. Безопасно ли изменять FormattedText в этом?

4. На ваш вопрос о возврате изящно, он возвращает нормально. Все, что мы делаем, это заменяем URL-адреса правильно экранированными URL-адресами, чтобы они правильно отображались в элементе управления. Может быть лучшая реализация; странно, что я не могу воссоздать всю свою жизнь, хотя я получаю отчеты. Либо это, либо это отвлекающий маневр. В любом случае спасибо за помощь. Очень признателен.

Ответ №1:

Похоже, что проблема, с которой вы столкнулись, связана с управлением памятью. Вы начинаете с поиска по строке self.formattedText . Это означает, что во время выполнения этого поиска вашему NSDataDetector экземпляру, вероятно, потребуется доступ к строке для чтения символов и т. Д. Это работает нормально и хорошо, пока self.formattedText не освобождается. Обычно, даже для блочных методов, подобных этому, вызывающий объект несет ответственность за сохранение аргументов до конца вызова функции.

Когда внутри вашего блока поиска соответствия вы изменяете значение self.formattedText , старое значение автоматически освобождается (при условии, что это retain свойство). Я не знаю о кэшировании, которое может выполнять NSDataDetector, Или о проблемах, связанных с пулами автоматического выпуска и т. Д., Но я почти уверен, что это может вызвать проблему.

я предлагаю, чтобы вы передавали [NSString stringWithString:self.formattedText] в качестве enumerateMatchesInString: аргумента, а не простого self.formattedText . Таким образом, вы передаете NSDataDetector экземпляр, который не будет выпущен до тех пор, пока пул авторелиза не будет исчерпан.

Комментарии:

1. Я не полностью протестировал это, но я думаю, что ваш ответ достоин. 🙂