NSURLConnection сбой под 10.5.7 - PullRequest
10 голосов
/ 27 мая 2009

У меня есть небольшое приложение, которое загружает цены на акции и прекрасно работало (в течение многих лет) до моего недавнего обновления до 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 делегат: 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. Сначала я подумал, что, возможно, автоматически освобожденный NSString escsymbol каким-то образом освобождается, но отправка сообщения сохранения не помогла. Если бы это было случаем, как я мог это доказать?

Кто-нибудь еще имеет эту проблему?

Ответы [ 8 ]

9 голосов
/ 01 июня 2009

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

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

Есть два возможных обходных пути:

  1. Не используйте URL-адреса перенаправления:
    Очевидно, что это не всегда возможно, но если это известный URL, вы все равно можете использовать простые вызовы (например, stringWithContentsOfURL:), и это будет работать нормально. Деннис, в вашем случае, правильный URL-адрес сервера download.finance.yahoo.com, а не finance.yahoo.com, поэтому я считаю, что это решит вашу конкретную проблему. Используя curl, вы можете видеть, что вы получаете перенаправление 301 при нажатии на последний адрес.
  2. Используйте асинхронные вызовы и реализуйте 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).

5 голосов
/ 29 мая 2009

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

РЕДАКТИРОВАТЬ: Кстати, предложение Колина о повторной реализации перенаправлений, похоже, не работает для NSURLDownload :(.

РЕДАКТИРОВАТЬ 2: Хорошо, это похоже на состояние гонки. Добавление cerr << "redirect" << endl; в обратном вызове "исправляет" это для меня с NSURLDownload. Сон в течение 1 секунды или блокировка локального статического мьютекса, однако, не имеют никакого эффекта ... </p>

4 голосов
/ 29 мая 2009

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

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

2 голосов
/ 02 июня 2009

Я также видел точно такой же сбой в 10.5.7 в нескольких приложениях.

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

2 голосов
/ 29 мая 2009

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

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

1 голос
/ 26 августа 2009

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

0 голосов
/ 28 мая 2009

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

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

0 голосов
/ 28 мая 2009

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

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

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

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