Хобрук: Ваш путь к мастерству в программировании

iOS — более быстрый способ загрузки UIImages из URL-адресов

У меня есть приложение, которое при первом открытии показывает всех ваших друзей и их изображения профилей и загружает UITableView с именами ваших друзей, именами пользователей и изображениями профиля в каждой ячейке. Проблема в том, что UITableView дольше загружает всех друзей, если у вас их много, около 30 например (занимает примерно 5-8 секунд). Я храню все эти изображения в словаре, связанном с именем пользователя друга, поэтому я могу повторно использовать их позже, но основная проблема заключается в том, сколько времени требуется для загрузки всех изображений при первом запуске приложения. Это код, который у меня есть на данный момент, который делает это, перебирая каждого друга:

[auth.loggedInUser getFriendsSuccessCallback:^(NSArray* friends){
    for (User* friend in friends) {
        if (![weakSelf.friendImageDictionary.allKeys containsObject:friend.userName])
        {
            UIImage *profilePicture;
            if (!friend.profilePicture) {
                profilePicture = [UIImage imageNamed:@"ProfilePic"];
            }
            else
            {
                NSURL *profilePictureURL = [NSURL URLWithString:friend.profilePicture];
                NSData * profilePictureData = [NSData dataWithContentsOfURL:profilePictureURL];
                profilePicture = [UIImage imageWithData:profilePictureData];
            }
            if (profilePicture == nil)
            {
                profilePicture = [UIImage imageNamed:@"ProfilePic"];
            }
            [weakSelf.friendImageDictionary setObject:profilePicture forKey:friend.userName];
        }
    }
    dispatch_async(dispatch_get_main_queue(), ^{
        [weakSelf.tableView reloadData];
    });
}

Любые советы о том, как значительно сократить время, необходимое для загрузки всех этих изображений?

ПРОГРЕСС:

Итак, я использую AFNetworking со следующим кодом:

[auth.loggedInUser getFriendsSuccessCallback:^(NSArray* friends){
    NSMutableArray *profilePictureRequests = [[NSMutableArray alloc] init];
    for (User* friend in friends) {
        if (![weakSelf.friendImageDictionary.allKeys containsObject:friend.userName])
        {
            if (!friend.profilePicture) {
                UIImage *profilePicture = [UIImage imageNamed:@"ProfilePic"];
                [weakSelf.friendImageDictionary setObject:profilePicture forKey:friend.userName];
            }
            else
            {
                NSURL *profilePictureURL = [NSURL URLWithString:friend.profilePicture];
                NSURLRequest *urlRequest = [NSURLRequest requestWithURL:profilePictureURL];
                AFHTTPRequestOperation *requestOperation = [[AFHTTPRequestOperation alloc] initWithRequest:urlRequest];
                requestOperation.responseSerializer = [AFImageResponseSerializer serializer];
                [requestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, UIImage* profilePicture) {
                    NSLog(@"Response: %@", profilePicture);
                    if (profilePicture == nil)
                    {
                        profilePicture = [UIImage imageNamed:@"ProfilePic"];
                    }
                    [weakSelf.friendImageDictionary setObject:profilePicture forKey:friend.userName]; 
                } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
                    NSLog(@"Image error: %@", error);
                }];
                [profilePictureRequests addObject:requestOperation];
            }
        }
    }
    for (AFHTTPRequestOperation *requestOperation in profilePictureRequests)
    {
        [requestOperation start];
    }
    dispatch_async(dispatch_get_main_queue(), ^{
        [weakSelf.tableView reloadData];
        weakSelf.dashboardViewController.loadingCoverView.hidden = YES;
        [weakSelf.dashboardViewController.tableActivityIndicator stopAnimating];
        NSLog(@"FINISHED LOADING ALL PROFILE PICTURES");
    });
}

По сути, я сохранял AFHTTPRequestOperation в NSMutableArray для каждого изображения, которое нужно было загрузить. После этого я перебрал NSMutableArray и начал каждый AFHTTPRequestOperation.

ВОПРОСЫ:

Это просто сделало загрузку всех изображений ЧРЕЗВЫЧАЙНО быстрой, но я думаю, что делаю это неправильно. Мое представление загрузки с индикатором активности завершается и исчезает ДО того, как все изображения будут загружены и добавлены в мой NSMutableDictionary, когда это должно происходить ПОСЛЕ того, как все они будут загружены и добавлены. Есть ли способ сделать так, чтобы окно загрузки и индикатор активности исчезли ПОСЛЕ завершения всех UIImage фоновых потоков загрузки?

ДОПОЛНИТЕЛЬНЫЙ ВОПРОС О AFNetworking:

Является ли то, как я это делаю (в этом случае одновременно выполняется около 30 фоновых потоков загрузки изображений), не является хорошей практикой с AFNetworking? Приведет ли это к будущим проблемам, если, скажем, у пользователя более 100 друзей и одновременно происходит более 100 фоновых потоков загрузки изображений?


  • Похоже, вам следует уменьшить размер серверной части изображений. 18.10.2015
  • Это было сделано, и время загрузки большого количества изображений по-прежнему остается проблемой, особенно когда у пользователей будет больше друзей. Я думаю, что в диапазоне установки нового фонового потока для каждого изображения, которое должно быть загружено с URL-адреса. Это возможно? 18.10.2015
  • Использовать AFNetworking и асинхронные загрузки довольно просто 18.10.2015
  • @james---›Я начал использовать AFNetworking, который помог, но у меня все еще возникают проблемы, которые я включил, когда я обновил свой вопрос с моим прогрессом. Не могли бы вы проверить это? 20.10.2015

Ответы:


1

Вы, вероятно, не уменьшите время загрузки, но вы можете сделать его быстрее.

Вы используете синхронные загрузки в основном потоке, очень плохая идея.

Существуют библиотеки, такие как AFNetworking, которые упрощают настройку асинхронных загрузок. Или, если вы хотите сделать это самостоятельно, вы можете использовать NSURLSession или NSURLSession (который устарел в iOS 9).

18.10.2015
  • Мой фрагмент кода находится в блоке вызова API к моему серверу, поэтому технически я загружаю изображения из фонового потока. Я очень быстро обновлю свой код. 18.10.2015
  • Вы уверены, что успешный обратный вызов происходит в основном потоке? Вы можете использовать NSThread isMainThread, чтобы сообщить. Добавьте оператор отладки, подобный этому, внутри обратного вызова успеха: NSLog("isMainThread = %d", [NSThread isMainThread]); 18.10.2015
  • Вероятно, вам следует реорганизовать свой код, чтобы загружать по одному изображению за раз и обновлять пользовательский интерфейс по мере загрузки каждого изображения. Такой фреймворк, как AFNetworking, делает это намного проще. 18.10.2015
  • @DuncanC---› Да, я запустил этот фрагмент, и мой код работает в фоновом потоке, за исключением случаев, когда я вношу изменения в пользовательский интерфейс, например, перезагружаю табличное представление. Я начал использовать AFNetworking, что помогло, но у меня все еще возникают проблемы, которые я включил, когда обновил свой вопрос с моим прогрессом. Не могли бы вы проверить это? 19.10.2015

  • 2

    При работе с UIImages я часто использую SDWebImage: https://github.com/rs/SDWebImage

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

    Пример его использования:

    #import <SDWebImage/UIImageView+WebCache.h>
    
    [self.imageView sd_setImageWithURL:[NSURL URLWithString:imageURL]
    
    18.10.2015
  • Итак, если я воспользуюсь этой библиотекой и включу этот оператор в цикл for, например, будут ли изображения загружаться все одновременно в разные потоки или цикл for будет ждать загрузки изображения до перехода к следующей итерации? 19.10.2015
  • Основное использование SDwebImage заключается в том, что он сохраняет изображения в кеше, чтобы через секунду они загружались из кеша, и мы очищаем кеш, когда вы предупреждаете о памяти. 01.04.2016
  • Новые материалы

    Получение стоковых обновлений с помощью Python
    Для начинающего финансового аналитика Введение Описание: Этот проект Python создает скрипт для получения текущих обновлений акций с финансового веб-сайта Yahoo. Для этого проекта мы..

    Это все, что вам нужно знать о Kotlin в 2022 году
    Добро пожаловать! Kotlin — это язык программирования, популярность которого, кажется, растет, его действительно можно использовать для создания чего угодно, и если вы хотите узнать о Kotlin,..

    Текстовый графический интерфейс с Lanterna на Java
    Мой опыт работы с компьютерами (и текстовыми графическими пользовательскими интерфейсами) начался еще в восьмидесятых, когда я был ребенком, на дне рождения друга. Это был «новенький» Amstrad..

    Перезарядите свой мозг: умопомрачительный потенциал мозговых компьютерных интерфейсов
    Способность читать свои мысли и управлять объектами разумом долгое время были предметом человеческого любопытства, ограниченного областью научной фантастики… то есть до сих пор? С технологией,..

    Основы C# — Нулевой оператор объединения (??)
    Оператор ?? называется null-coalescing operator . Этот оператор используется для предоставления значения по умолчанию, если значение операнда в левой части оператора равно null ...

    Сравнение номеров версий в C++ с использованием синтаксического анализа строк
    Номера версий обычно используются для обозначения развития или обновлений программного обеспечения или любого другого продукта. При работе с номерами версий в C++ может быть полезно сравнить две..

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