Лучший способ обрабатывать много ASIHttpRequest и операций с основными данными в приложении ipad - PullRequest
3 голосов
/ 20 мая 2011

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

Сценарий следующий - приложению необходимо загрузить изображения для объектов, которые я отображаю в виде сетки, которая может отображать в общей сложности 30 объектов. Каждый объект может содержать до 15 изображений PNG (также в виде сетки). В связи с тем, как реализован сервер (то есть я не реализовал его и не могу его легко изменить), каждое изображение необходимо запрашивать отдельно, поэтому мне нужно сделать до 15 запросов на объект вместо одного запроса на загрузку всех 15 изображений.

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

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

Одним из решений, с которым я столкнулся, было выполнение основных операций с данными и запись в отдельном потоке или с использованием центральной центральной диспетчеризации. Другой способ - загружать изображения только для видимых объектов и загружать остальные, когда пользователь прокручивает страницу вниз.

Я ищу другие предложения, чтобы помочь адаптировать основной интерфейс или более эффективные способы структурирования сети и операций с основными данными. Спасибо.

Ответы [ 3 ]

5 голосов
/ 14 июня 2011

Прежде всего, избегайте хранения больших двоичных объектов в Базовых данных, сохраняя миниатюры в порядке (хотя вы должны оптимизировать свою модель для этого), но вы должны сохранить полное изображение после его восстановления в папке «Документы».1002 * Вы обязательно должны использовать очередь, либо NSOperationQueue, либо сетевую очередь ASI.Я делаю нечто подобное в моем приложении, которое имеет несколько зависимостей.Таким образом, для каждого из 30 объектов вам нужно, чтобы блок (или рабочая функция) вызывался после загрузки 15 изображений.В идеале вы хотите сделать эту работу вне основного потока.Соберите все эти требования вместе, и я бы сказал, что вам нужно как минимум две очереди, одна для сетевых запросов и одна для рабочих блоков, и вы должны использовать NSBlockOperations, что делает все это намного проще.Итак, код будет выглядеть примерно так ...

// Loop through the objects
for (NSArray *objectParts in objectsToDownload) {

    // Create our Object
    Object *obj = [Object insertIntoManagedObjectContext:self.moc];

    // This is the block which will do the post processing for the object
    NSBlockOperation *processBlock = [NSBlockOperation blockOperationWithBlock:^{

        // Do post processing here, be very careful with multi-threading CoreData
        // it's likely you'll need some class to dispatch you MOCs which have all
        // all the observers set up.

        // We're gonna assume that all the sub-images have been stored in a instance
        // variable:
        [obj performPostProcessing];

    }];

    // Given the list of 15 images which form each object
    for (NSURL *part in objectParts) {

        // Create the ASI request for this part
        ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:part];

        // Configure the request
        [request setDelegate:self];
        [request setDidFailSelector:@selector(partRequestDidFail:)];
        [request setDidFinishSelector:@selector(partRequestDidFinish:)];

        // Store the object in the UserInfo dictionary
        NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:obj, @"Object", nil];
        [request setUserInfo:userInfo];

        // Add it as a dependency
        [processBlock addDependency:request];

        // Add it to our network queue
        [networkQueue addOperation:request];
    }

    // Add the processBlock to our worker queue
    [workerQueue addOperation:processBlock];
}

Тогда вам также необходимо написать методы делегата, а didFinish будет выглядеть примерно так ...

- (void)partRequestDidFinish:(ASIHTTPRequest *)request {
    // Remember this is one the main thread, so any heavy lifting should be 
    // put inside a block operation, and queued, which will complicate the 
    // dependencies somewhat, but is possible.

    // Get the result data
    NSData *data = [request responseData];

    // Get the object that it belongs to from the user info dic
    Object *obj = [[request userInfo] objectForKey:@"Object"];

    // Keep track of the partial data in the object
    [obj storePartialDataForPostProcessing:data];
}

И все это попадет в ваш класс, который подключается к вашему серверу и создает ваши объекты, так что это не контроллер представления или что-то еще, а обычный подкласс NSObject.У него должно быть две очереди, контекст управляемого объекта (и, скорее всего, метод, который возвращает другой MOC для использования в потоках, что-то вроде этого:

// Fends a MOC suitable for use in the NSBlockOperations
- (NSManagedObjectContext *)moc {
    // Get a blank managed object context
    NSManagedObjectContext *aContext = [[UIApplication sharedApplication] managedObjectContext;
[aContext setMergePolicy:NSMergeByPropertyStoreTrumpMergePolicy];
    NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
    [nc addObserver:self selector:@selector(mergeChangesFromMOC:) name:NSManagedObjectContextDidSaveNotification object:aContext];
    return aContext;

}

- (void)mergeChangesFromMOC:(NSNotification *)aNotification {
    @try {
        [self.managedObjectContext mergeChangesFromContextDidSaveNotification:aNotification];
        NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
        [nc removeObserver:self name:NSManagedObjectContextDidSaveNotification object:[aNotification object]];      
    }
    @catch (NSException * e) {
        NSLog(@"Stopping on exception: %@", [e description]);
    }
    @finally {}
}

Вам также нужно будет каким-то образом отслеживать ход выполнения, ставить в очередь неудачные загрузки, отменять и сохранять MOC в конце. Повторное постановка неудачных загрузок довольно сложно. В любом случае, надеюсь, это поможет.

Итак, просто чтобы уточнить, что в вашем методе делегата вы сохранили загруженный образ во временной переменной экземпляра вашего объекта. Затем, когда все 15 зависимостей закончатся, вы сможете получить доступ к этой переменной экземпляра и выполнять свою работу.1017 *

0 голосов
/ 09 марта 2012

Так как вы будете отображать изображение для просмотра, почему бы вам не SDwebImage:

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

[imageView setImageWithURL: [NSURL URLWithString: @ "http://www.domain.com/path/to/image.jpg"] placeholderImage: [UIImage imageNamed: @" placeholder.png "]]

https://github.com/rs/SDWebImage

0 голосов
/ 23 мая 2011

Чтобы начать, 1 очереди должно быть достаточно для всех запросов изображения.

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

Для блокировки есть несколько вещей, которые следует учитывать при работе с изображениями:

  1. Они сжаты, поэтому их необходимо надуть перед тем, как получить UIImages, что очень сильно загружает ЦП.
  2. Если вы когда-нибудь захотите записать в файловую систему, этот процесс блокируется.Сделайте это в другой очереди, чтобы избежать блокировки.
  3. Никогда не рекомендуется хранить Blob в CoreData, сохранять путь к файлу в виде строки в Core Data и извлекать его с диска, используя очередь

Простое использование 3 разных NSOperationQueue сделает ваше приложение намного более отзывчивым: 1 для ASIHTTPRequests (не создавайте новый, используйте по умолчанию с startAsynchronous) 1 для записи образа на диск 1 для извлечения образа с диска

...