Сбой NSURLConnection под 10.5.7

У меня есть небольшое приложение, которое загружает цены на акции и работает отлично (в течение многих лет) до моего недавнего обновления до 10.5.7. После обновления программа выйдет из строя при этом вызове:

NSString *currinfo = [NSString stringWithContentsOfURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://finance.yahoo.com/d/quotes.csv?s=%@&f=l1c1p2", escsymbol]]];

Как ни странно, крушение не происходит сразу. Эта строка кода вызывается много раз, без проблем, а затем программа в итоге выходит из строя через 1-2 часа из-за сбоя в этом вызове.

У меня первоначально был длинный пост, описывающий мои попытки исследовать эту проблему. Я получил два предложения: (i) сделать асинхронный вызов (возможно, в любом случае лучше) и (ii) использовать NSZombieEnabled, чтобы исследовать возможность того, что объект Objective-C будет освобожден раньше (этот комментарий был сделан в ответ на следы стека, показывающие сбой в objc_msgSend).

Я потратил много времени на то, чтобы сделать асинхронный вызов (используя [[NSURLConnection alloc] initWithRequest: theRequest delegate: self]), и это не помогло. Программа по-прежнему не срабатывала в конце концов, обычно через 10-15 минут. В течение этого интервала перед сбоем многие асинхронные вызовы были сделаны без каких-либо проблем, данные были возвращены и т.д. Все было в порядке. Затем программа снова разбилась.

Затем я включил NSZombieEnabled. Разумеется, когда программа в конечном итоге потерпела крах, я получил сообщение:

-[CFArray count]: message sent to deallocated instance 0x16b90bd0

"info malloc 0x16b90bd0" затем дал:

0: 0x93db810c in malloc_zone_malloc
1: 0x946bc3d1 in _CFRuntimeCreateInstance
2: 0x9464a138 in __CFArrayInit
3: 0x946cd647 in _CFStreamScheduleWithRunLoop
4: 0x932d1267 in _Z16_scheduleRStreamPKvPv
5: 0x946bf15c in CFSetApplyFunction
6: 0x932b0e2b in CFNSchedulingSetScheduleReadStream
7: 0x9331a310 in _ZN12HTTPProtocol19createAndOpenStreamEv
8: 0x9332e877 in _ZN19URLConnectionLoader24loaderScheduleOriginLoadEPK13_CFURLRequest
9: 0x9332d739 in _ZN19URLConnectionLoader26LoaderConnectionEventQueue33processAllEventsAndConsumePayloadEP20XConnectionEventInfoI12XLoaderEvent18XLoaderEventParamsEl
10: 0x9332dbdd in _ZN19URLConnectionLoader13processEventsEv
11: 0x932d8dbf in _ZN17MultiplexerSource7performEv
12: 0x946ba595 in CFRunLoopRunSpecific
13: 0x946bac78 in CFRunLoopRunInMode
14: 0x9058c530 in +[NSURLConnection(NSURLConnectionReallyInternal) _resourceLoadLoop:]
15: 0x90528e0d in -[NSThread main]
16: 0x905289b4 in __NSThread__main__
17: 0x93de8155 in _pthread_start
18: 0x93de8012 in thread_start

Я не эксперт в чтении стеков стека, но разве эта трассировка не указывает на проблему в коде Apple, а не на мой код? Или я могу как-то нести ответственность за де-распределение рассматриваемого CFArray? Есть ли способ для дальнейшего изучения причины проблемы?


(Здесь остальная часть моего первоначального сообщения)

Увидев, что stringWithContentsOfURL устарел, я переключился на этот код:

pathURL = [NSURL URLWithString:[NSString stringWithFormat:@"http://finance.yahoo.com/d/quotes.csv?s=%@&f=l1c1p2", escsymbol]];

NSURLRequest *request = [NSURLRequest requestWithURL:pathURL cachePolicy:NSURLRequestReturnCacheDataElseLoad timeoutInterval:30.0];

responseData = [ NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];

NSString *currinfo = nil;

if ([error code]) {  dNSLog((@"%@ %d %@ %@ %@", [ error domain], [ error code], [ error localizedDescription], request, @"file://localhost/etc/gettytab"));  }

Это не помогло. Программа по-прежнему выходит из строя в строке sendSynchronousRequest через произвольный промежуток времени, с этой информацией в отладчике:

0   0x93db7286 in mach_msg_trap
1   0x93dbea7c in mach_msg
2   0x946ba04e in CFRunLoopRunSpecific
3   0x946bac78 in CFRunLoopRunInMode
4   0x932b53eb in CFURLConnectionSendSynchronousRequest
5   0x905dca4b in +[NSURLConnection sendSynchronousRequest:returningResponse:error:]

... и т.д..

Реальная авария может быть в другом потоке:

0   libobjc.A.dylib                 0x965c3688 objc_msgSend + 24
1   com.apple.CoreFoundation        0x946cc581 _CFStreamSignalEventSynch + 193
2   com.apple.CoreFoundation        0x946ba595 CFRunLoopRunSpecific + 3141
3   com.apple.CoreFoundation        0x946bac78 CFRunLoopRunInMode + 88
4   com.apple.Foundation            0x9058c530 +[NSURLConnection(NSURLConnectionReallyInternal) _resourceLoadLoop:] + 320
5   com.apple.Foundation            0x90528e0d -[NSThread main] + 45
6   com.apple.Foundation            0x905289b4 __NSThread__main__ + 308
7   libSystem.B.dylib               0x93de8155 _pthread_start + 321
8   libSystem.B.dylib               0x93de8012 thread_start + 34

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

Это невероятно расстраивает. Я был бы очень рад потратить столько времени, сколько необходимо, чтобы выявить проблему, но я как бы не согласен с моими знаниями с gdb и особенно с ассемблером. Я не знаю, как узнать, какова фактическая проблема для кода Foundation. Сначала я подумал, что, возможно, autoreleased NSString escsymbol как-то освобождается, но отправка его сообщения сохранения не помогло. Если бы это было так, как я мог это доказать?

Есть ли у кого-нибудь еще эта проблема?

Ответ 1

Я думаю, что ответ Евгений в этом потоке правильно описывает проблему; после некоторого тестирования, вот что я сделал, похоже, происходит, надеюсь, некоторые подробности, чтобы помочь другим застрять в этой проблеме:

Перенаправление URL-адресов будет периодически вызывать сбои. Это происходит как при синхронизации, так и при асинхронном использовании NSURLConnection. Я создал тестовый проект для отслеживания этой ошибки, и эта авария будет постоянно возникать (обычно между 25-500 итерациями). Выполнение того же теста на 10.5.6 или без перенаправления URL-адресов не сработает (запустило до 20 000 итераций).

Существует два возможных варианта работы:

  • Не используйте перенаправляющие URL-адреса:
    Очевидно, что это не всегда возможно, но если это известный URL, вы все равно можете использовать простые вызовы (например, stringWithContentsOfURL:), и это будет работать нормально. Деннис, в вашем случае, правильный URL-адрес сервера download.finance.yahoo.com, а не finance.yahoo.com, поэтому я считаю, что это устранит вашу конкретную проблему. Используя curl, вы можете увидеть, что вы получаете перенаправление 301, когда вы нажимаете последний адрес.
  • Использовать асинхронные вызовы и реализовать connection:willSendRequest:redirectResponse:
    Если вы реализуете даже самую основную обработку этого метода делегата, все работает. Если вы оставите этот вызов (и, таким образом, пусть система использует свою реализацию по умолчанию), вы получите сообщение об ошибке. Для базовой реализации просто верните запрос, который передается:
- (NSURLRequest *)connection:(NSURLConnection *)connection 
             willSendRequest:(NSURLRequest *)request 
            redirectResponse:(NSURLResponse *) redirectResponse
{
    return request;
}

Все это, кажется, подсказывает мне, что в версии Apple реализовано что-то, что произошло в версии 10.5.7, но если кто-то еще имеет представление о том, что может происходить, пожалуйста, перезвоните.

Я отправил ошибку с моим тестовым проектом в Apple как rdar://6936109 и ссылался на отчет tjw (Radar 6932684).

Ответ 2

Это похоже на перенаправление. Мое приложение, загружающее около 500 мегабайт данных напрямую (несколько сотен отдельных файлов), не падает. Такое же приложение, загружающее меньший набор URL-адресов, все из которых перенаправлены, будет случайным образом разбиваться несколько раз именно в этой точке (возобновление возобновления и повторный запуск снова приведет к успешной загрузке).

EDIT: BTW, предложение Колина о переопределении переадресаций, похоже, не работает для NSURLDownload:(.

EDIT2: Хорошо, это похоже на состояние гонки. Добавление cerr < "перенаправление" < епсИ; в обратном вызове "исправляет" это для меня с NSURLDownload. Спящий режим в течение 1 секунды или блокировка локального статического мьютекса не имеют эффекта...

Ответ 3

Сбой в objc_msgSend обычно происходит из-за неправильного жизненного цикла объекта (т.е. объект был выпущен и отправляется сообщение).

Запустите свой код с NSZombieEnabled, чтобы выяснить, действительно ли это ваша проблема и посмотреть, какой объект выпускается раньше.

Ответ 4

В The Omni Group мы видим этот новый крах в 10.5.7 во всех наших приложениях, использующих NSURL, тоже при асинхронном использовании (поэтому я считаю, что синхронная вещь - красная селедка). Мы также имели это в TextMate (предположительно в обновлении программного обеспечения).

Я зарегистрировал Radar 6932684 по этой проблеме, и если вы тоже это видите, я настоятельно рекомендую всем вам сообщить об этом, какие детали вы можете собрать.

Ответ 5

Я также видел эту ту же ошибку, которая появилась в 10.5.7 в нескольких приложениях.

Учитывая, что реализация простейшего метода делегата connection:willSendRequest:redirectResponse: решит проблему, я думаю, может быть очень связана с радар # 6700222.

Ответ 6

Для чего это стоит, это, по-видимому, исправлено на 10.5.8 --- но оно было заменено новой ошибкой, которая добавляет перенаправление тела ответа на реальный ответ примерно в 2% времени. (Легко воспроизводить, вращаясь на -stringWithContentsOfURL:, но мы также видели это в дикой природе.) Сообщалось о том, что ошибка как радар # 7169953.

Ответ 7

Я бы предложил не использовать синхронные URL-соединения. Это требует некоторой реструктуризации кода, но это действительно плохое поведение для блокировки основного потока в сети. (Предполагая, что вы делаете это в основном потоке).

Кроме того, я предполагаю, что это код, который Apple планирует осудить или прекратить поддерживать, что может быть тем, что вы видите здесь.

Надеюсь, что это поможет....

Ответ 8

Мы видим ту же проблему, используя как устаревшие stringWithContentsOfURL, так и sendSynchronousRequest (в разных местах). Мы видим тот же тип поведения; вызов может работать нормально для многих итераций, а затем будет случайно сбой (по идентичным запросам).

Учитывая, что синхронные вызовы построены поверх механизма async, нет причин, по которым эти вызовы не должны работать должным образом. К сожалению, у нас пока нет решения.