zoukankan      html  css  js  c++  java
  • iOS多线程自定义operation加载图片 不重复下载图片

    摘要:1:ios通过抽象类NSOperation封装了gcd,让ios的多线程变得更为简单易用;

         2:耗时的操作交给子线程来完成,主线程负责ui的处理,提示用户的体验

         2:自定义operation继承自NSOperation,在子线程中下载图片;

       3:保证图片只下载一次,还有保证下载任务不重复

    ------------------------------------------------------------------------------------

    实现原理:1:图片缓存:用字典保存图片和图片的url,key:url  value:图片

         2:任务缓存:用字典保存operation任务和图片的url,key:url  value:operation

    先看自定义的operation:.h文件

     1 #import <Foundation/Foundation.h>
     2 @class XBOperation;
     3 
     4 @protocol XBOperationDelegate <NSObject>
     5 
     6 -(void)operation:(XBOperation *)operation didFinishDownloadImage:(UIImage *)image;
     7 
     8 @end
     9 /**
    10  *  自定义operation
    11  */
    12 @interface XBOperation : NSOperation
    13 
    14 // 代理
    15 @property (nonatomic, weak)id<XBOperationDelegate> delegate;
    16 
    17 /**
    18  *  url
    19  */
    20 @property (nonatomic, copy) NSString *url;
    21 /**
    22  *  indexPath 和tableview中的cell相对应
    23  */
    24 @property (nonatomic, strong) NSIndexPath *indexPath;
    25 
    26 @end

    说明:设置代理的目的是等完成下载图片的后通知控制器拿图片

    自定义operation的.m文件

    @implementation XBOperation
    
    
    -(void)main
    {
        
        if (self.isCancelled) return;
        NSURL *downloadUrl = [NSURL URLWithString:self.url];
        NSData *data = [NSData dataWithContentsOfURL:downloadUrl]; // 这行会比较耗时
        
        if (self.isCancelled) return;
        
        UIImage *image = [UIImage imageWithData:data];
        
        if (self.isCancelled) return;
        NSLog(@"--%@--", [NSThread currentThread]);
        
        if ([self.delegate respondsToSelector:@selector(operation:didFinishDownloadImage:)]) {
            dispatch_async(dispatch_get_main_queue(), ^{ // 回到主线程, 传递图片数据给代理对象
                [self.delegate operation:self didFinishDownloadImage:image];
            });
        }
    }
    @end

    说明:重写main方法,在把任务operation加入到队列后,队列会自动调用任务的start方法,start方法内部会调用main方法来完成操作

    控制器的tableviewcell数据源的实现

    #pragma mark  dataSource数据源方法
    -(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
    {
        return 1;
    }
    
    -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    {
        return self.apps.count;
    }
    
    // 数据源方法 实现cell中的内容填充
    -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        static NSString *ID = @"app";
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
        if (!cell) {
            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
        }
    
        // 设置数据  XBApp是数据模型
        XBApp *app = self.apps[indexPath.row];
        
        // 设置标题和子标题
        cell.textLabel.text = app.name;
        cell.detailTextLabel.text = app.download;
        cell.imageView.image = [UIImage imageNamed:@"57437179_42489b0"];
        
        
        // 图片数据设置
        UIImage *image = self.imageCache[app.icon];
        
        if (image) { // 如果图片缓存中有图片 直接加载到
            cell.imageView.image = image;
        }else{ // 如果缓存中没有数据
            
            // 先设置占位占位图片
            cell.imageView.image = [UIImage imageNamed:@"57437179_42489b0"];
            
            // 创建自定义的operation 先根据当前要显示图片的url从队列中找到operation 看是否正在下载中
            XBOperation *operationDown = self.operationQueue[app.icon];
            if (!operationDown) { // 如果下载operation不存在 就创建 并添加到队列中
                operationDown = [[XBOperation alloc] init];
                operationDown.delegate = self; // 设置代理
                operationDown.indexPath = indexPath; // 把每个cell与一个operation绑定
                operationDown.url = app.icon;
                // 把operation添加到队列中 会自动调用start方法 然后调用operation的main的方法
                [self.queue addOperation:operationDown];
                // 把operation这个下载任务添加到operation的字典中 防止重复下载
                self.operationQueue[app.icon] = operationDown;
            }
        
        }
        
        
        return cell;
        
    }
    
    // 实现代理方法
    -(void)operation:(XBOperation *)operation didFinishDownloadImage:(UIImage *)image
    {
        // 必须清楚当前operation 防止由于网络等原因造成的下载失败 而operation还在字典中 这样永远下载不了
        [self.operationQueue removeObjectForKey:operation.url];
        
        if (image) {
            // 将图片放在缓存字典中
            self.imageCache[operation.url] = image;
            
            // 刷新表格 单行刷新
            [self.tableView reloadRowsAtIndexPaths:@[operation.indexPath] withRowAnimation:UITableViewRowAnimationNone];
        }
    }
  • 相关阅读:
    链表
    线程池 ------ linux C实现
    thymeleaf 标签使用方法
    thymeleaf的配置
    exception processing, template error resolving template
    Thymeleaf模板表达式
    Mybatis:使用bean传值,当传入值为Null时,提示“无效的列类型”的解决办法
    windows 查看端口
    session与cookie的区别
    substr与substring的区别
  • 原文地址:https://www.cnblogs.com/cxbblog/p/3809238.html
Copyright © 2011-2022 走看看