В моем приложении я выполняю 10 асинхронных NSURLConnections в NSOperationQueue как NSInvocationOperations. Чтобы предотвратить возврат каждой операции до того, как соединение сможет завершиться, я вызываю CFRunLoopRun (), как показано здесь:
- (void)connectInBackground:(NSURLRequest*)URLRequest {
TTURLConnection* connection = [[TTURLConnection alloc] initWithRequest:URLRequest delegate:self];
// Prevent the thread from exiting while the asynchronous connection completes the work. Delegate methods will
// continue the run loop when the connection is finished.
CFRunLoopRun();
[connection release];
}
Как только соединение завершается, последний селектор делегата соединения вызывает CFRunLoopStop (CFRunLoopGetCurrent ()), чтобы возобновить выполнение в connectInBackground (), позволяя ему нормально вернуться:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
TTURLConnection* ttConnection = (TTURLConnection*)connection;
...
// Resume execution where CFRunLoopRun() was called.
CFRunLoopStop(CFRunLoopGetCurrent());
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
TTURLConnection* ttConnection = (TTURLConnection*)connection;
...
// Resume execution where CFRunLoopRun() was called.
CFRunLoopStop(CFRunLoopGetCurrent());
}
Это работает хорошо и является поточно-ориентированным, потому что я связал ответ и данные каждого соединения как переменные экземпляра в подклассе TTURLConnection.
NSOperationQueue утверждает, что оставляя его максимальное количество одновременных операций как NSOperationQueueDefaultMaxConcurrentOperationCount позволяет ему динамически регулировать количество операций, однако в этом случае он всегда решает, что 1 достаточно. Поскольку это не то, чего я хочу, я изменил максимальное число на 10, и теперь оно серьезно тянет.
Проблема с этим заключается в том, что эти потоки (с помощью SpringBoard и DTMobileIS) потребляют все доступное время ЦП и приводят к тому, что основной поток становится латентным. Другими словами, как только процессор загружен на 100%, основной поток не обрабатывает события пользовательского интерфейса так быстро, как это необходимо для поддержания гладкого пользовательского интерфейса. В частности, прокрутка табличного представления становится нервной.
Process Name % CPU
SpringBoard 45.1
MyApp 33.8
DTMobileIS 12.2
...
Пока пользователь взаимодействует с экраном или прокручивает таблицу, приоритет основного потока становится равным 1,0 (максимально возможный), а режим его цикла выполнения становится UIEventTrackingMode. Каждый из потоков операции имеет приоритет 0,5 по умолчанию, и асинхронные соединения выполняются в NSDefaultRunLoopMode. Из-за моего ограниченного понимания того, как потоки и их циклы выполнения взаимодействуют в зависимости от приоритетов и режимов, я в тупике.
Есть ли способ безопасно использовать все доступное время ЦП в фоновых потоках моего приложения, при этом гарантируя, что его основной поток отдает столько ЦП, сколько ему нужно? Возможно, заставляя основной поток запускаться так часто, как это необходимо? (Я думал, что приоритеты нити позаботятся об этом.)
ОБНОВЛЕНИЕ 12/23:
Я наконец-то начал разбираться с процессором Sampler и нашел большинство причин, по которым интерфейс стал нервным. Во-первых, мое программное обеспечение вызывало библиотеку, в которой были семафоры взаимного исключения. Эти блокировки блокировали основной поток на короткие промежутки времени, вызывая незначительное пропускание прокрутки.
Кроме того, я нашел несколько дорогих вызовов NSFileManager и хэш-функций md5, которые выполнялись слишком долго. Слишком частое выделение больших объектов вызывало некоторые другие падения производительности в основном потоке.
Я начал решать эти проблемы, и производительность уже намного лучше, чем раньше. У меня 5 одновременных подключений, и прокрутка плавная, но у меня еще есть над чем поработать. Я планирую написать руководство по использованию CPU Sampler для выявления и устранения проблем, влияющих на производительность основного потока. Спасибо за комментарии, они были полезны!
ОБНОВЛЕНИЕ 14.01.2010:
После достижения приемлемой производительности я начал понимать, что инфраструктура CFNetwork иногда теряла память. Исключения были случайным образом (но редко), возникающим и внутри CFNetwork! Я старался изо всех сил, чтобы избежать этих проблем, но ничего не получалось. Я совершенно уверен, что проблемы связаны с дефектами внутри самого NSURLConnection. Я написал тестовые программы, которые ничего не делали, кроме упражнения NSURLConnection, и они все еще не работали.
В итоге я заменил NSURLConnection на ASIHTTPRequest , и сбой полностью прекратился. CFNetwork почти никогда не протекает, однако есть еще одна очень редкая утечка, которая возникает при разрешении DNS-имени. Я вполне доволен сейчас. Надеюсь, эта информация сэкономит вам время!