【问题标题】:How to get array of images from network faster? (iOS)如何更快地从网络获取图像数组? (iOS)
【发布时间】:2013-07-30 23:18:27
【问题描述】:

基本上,我有一个作为字符串的 url 数组,当我遍历这个数组时,如果元素是图像的 url,我想将该 url 转换为 UIImage 对象并将其添加到另一个数组中。这很慢,因为我必须为每个 URL 请求数据。我已经尝试使用如下所示的 dispatch_async,但它似乎根本没有任何区别。

关键是当我将这些对象添加到我的另一个数组时,无论它们是图像还是其他东西,它们都必须保持有序。任何人都可以提供任何指导吗?

dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (int i=0; i<[slides count]; i++){
        __block NSString *mediaURLString = [primaryPhoto objectForKey:@"url"];

        if ([self mediaIsVideo:mediaURLString]){
              ***some code***
        }
        else{ //if media is an image
            dispatch_group_async(group, queue, ^{

                mediaURLString = [mediaURLString stringByAppendingString:@"?w=1285&h=750&q=150"];
                NSURL *url = [NSURL URLWithString:mediaURLString];
                [mutableMedia addObject:url];
                NSURL *url = ((NSURL *)self.mediaItem);
                NSURLRequest *request = [NSURLRequest requestWithURL:url];
                NSURLResponse *response;
                NSError *error;
                NSData *urlData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];

                UIImage *image = [[UIImage alloc] initWithData:urlData];
                [mutableMedia replaceObjectAtIndex:i withObject:image];
            });
        }
    }
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

【问题讨论】:

    标签: objective-c asynchronous nsurlconnection grand-central-dispatch nsurlrequest


    【解决方案1】:

    试试这个:

    [self performSelectorInBackground:@selector(WebServiceCallMethod) withObject:nil];
    

    并创建一个这样的方法

    -(void)WebServiceCallMethod
    {
                    mediaURLString = [mediaURLString stringByAppendingString:@"?w=1285&h=750&q=150"];
                    NSURL *url = [NSURL URLWithString:mediaURLString];
                    [mutableMedia addObject:url];
                    NSURL *url = ((NSURL *)self.mediaItem);
                    NSURLRequest *request = [NSURLRequest requestWithURL:url];
                    NSURLResponse *response;
                    NSError *error;
                    NSData *urlData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
    
                    UIImage *image = [[UIImage alloc] initWithData:urlData];
                    [mutableMedia replaceObjectAtIndex:i withObject:image];
    }
    

    希望对你有帮助!!

    【讨论】:

      【解决方案2】:

      帮自己一个忙,不要使用+sendSynchronousRequest:... 试试这样的东西:

      dispatch_group_t group = dispatch_group_create();
      for (int i=0; i<[slides count]; i++)
      {
          __block NSString *mediaURLString = [primaryPhoto objectForKey:@"url"];
          if ([self mediaIsVideo:mediaURLString]){
              ***some code***
          }
          else
          {
              //if media is an image
              mediaURLString = [mediaURLString stringByAppendingString:@"?w=1285&h=750&q=150"];
              NSURL *url = [NSURL URLWithString:mediaURLString];
              [mutableMedia addObject:url];
              NSURL *url = ((NSURL *)self.mediaItem);
              NSURLRequest *request = [NSURLRequest requestWithURL:url];
              dispatch_group_enter(group);
              [NSURLConnection sendAsynchronousRequest: request queue: [NSOperationQueue mainQueue] completionHandler:
               ^(NSURLResponse *response, NSData *data, NSError *connectionError)
               {
                   if (data.length && nil == connectionError)
                   {
                       UIImage *image = [[UIImage alloc] initWithData:data];
                       [mutableMedia replaceObjectAtIndex:i withObject:image];
                   }
                   dispatch_group_leave(group);
               }];
          }
      }
      
      dispatch_group_notify(group, dispatch_get_main_queue(), ^{
          // Do stuff here that you want to have happen after all the images are loaded.
      });
      

      这将为您的所有网址启动异步请求。当每个请求完成时,它将运行其完成处理程序,该处理程序将更新您的数组,当所有请求完成时,将执行 dispatch_group_notify 调用中的块。

      这种方法的优点是您可以从主线程调用它,所有单独的完成块都将在主线程上运行(从而确保 mutableMedia 数组的线程安全(至少就这段代码而言) )) 并且最终的完成块也将在主线程上运行,因此您可以执行任何需要直接更新 UI 的操作。

      【讨论】:

        【解决方案3】:

        有一个使用 dispatch lib 的绝妙解决方案。下面的代码应该代表自己。

        基本思想是数组包含“输入”对象,每个对象都将通过异步一元任务“转换” - 一个接一个。整个操作的最终结果是转换后的对象数组。

        这里的一切都是异步的。每个最终结果都将在完成处理程序中传递,这是一个块,结果作为参数传递给调用站点:

        typedef void (^completion_t)(id result);
        

        异步转换函数是将输入作为参数并通过完成处理程序返回一个新对象的块:

        typedef void (^unary_async_t)(id input, completion_t completion);
        

        现在,函数transformEach 将输入值作为NSArray 参数inArray,将转换块作为参数transform,将完成处理程序块作为参数完成:

        static void transformEach(NSArray* inArray, unary_async_t transform, completion_t completion);
        

        实现如下:

        static void do_each(NSEnumerator* iter, unary_async_t transform, 
          NSMutableArray* outArray, completion_t completion)
        {
            id obj = [iter nextObject];
            if (obj == nil) {
                if (completion)
                    completion([outArray copy]);
                return;
            }
            transform(obj, ^(id result){
                [outArray addObject:result];
                do_each(iter, transform, outArray, completion);
            });
        }
        
        static void transformEach(NSArray* inArray, unary_async_t transform, 
          completion_t completion) {
            NSMutableArray* outArray = [[NSMutableArray alloc] initWithCapacity:[inArray count]];
            NSEnumerator* iter = [inArray objectEnumerator];
            do_each(iter, transform, outArray, completion);
        }
        

        并构建并运行以下示例

        int main(int argc, const char * argv[])
        {
            @autoreleasepool {
        
                // Example transform:
                unary_async_t capitalize = ^(id input, completion_t completion) {
                    dispatch_async(dispatch_get_global_queue(0, 0), ^{
                       sleep(1);
                        if ([input respondsToSelector:@selector(capitalizedString)]) {
                            NSLog(@"processing: %@", input);
                            NSString* result = [input capitalizedString];
                            if (completion)
                                completion(result);
                        }
                    });
                };
        
        
                transformEach(@[@"a", @"b", @"c"], capitalize, ^(id result){
                    NSLog(@"Result: %@", result);
                });
                sleep(10);        
            }
            return 0;
        }
        

        将其打印到控制台:

        2013-07-31 15:52:49.786 Sample2[1651:1603] processing: a
        2013-07-31 15:52:50.789 Sample2[1651:1603] processing: b
        2013-07-31 15:52:51.792 Sample2[1651:1603] processing: c
        2013-07-31 15:52:51.793 Sample2[1651:1603] Result: (
            A,
            B,
            C
        )
        

        您可以轻松地为 NSArray 创建一个类别,它实现了,比如

        -(void) asyncTransformEachWithTransform:(unary_async_t)transform completion:(completion_t)completionHandler;

        方法。

        玩得开心! ;)


        编辑:

        如果你问这如何适用于你的问题:

        URL 数组是输入数组。为了创建转换块,只需将您的 asynchronous 网络请求包装在 asynchronous 方法中,例如:

        `-(void) fetchImageWithURL:(NSURL*)url completion:(completion_t)completionHandler;`
        

        然后将方法fetchImageWithURL:completion:包装到适当的变换块中:

         unary_async_t fetchImage = ^(id url, completion_t completion) {
            [self fetchImageWithURL:url completion:^(id image){
                if (completion) 
                    completion(image);  // return result of fetch request
            }];
        };
        

        然后,假设您为 NSArray 实现了类别,并且您的 url 数组是属性 urls,在您的代码中的某处(可能是视图控制器):

        // get the images
        [self.urls asyncTransformEachWithTransform:fetchImage completion:^(id arrayOfImages) {
             // do something with the array of images
        }];
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2022-07-07
          • 2012-09-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-05-29
          • 2019-04-10
          • 1970-01-01
          相关资源
          最近更新 更多