NSOperation下载图片-02
有沙盒缓存
1 #import "HMAppViewController.h" 2 #import "HMApp.h" 3 4 #define HMAppImageFile(url) [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:[url lastPathComponent]] 5 6 @interface HMAppViewController () 7 /** 8 * 模型数组 9 */ 10 @property (nonatomic, strong) NSArray *apps; 11 12 /** 13 * 存储icon的url的字典 14 */ 15 @property (nonatomic, strong) NSMutableDictionary *operations; 16 17 /** 18 * 全局队列 19 */ 20 @property (nonatomic, strong) NSOperationQueue *queue; 21 22 /** 23 * 存储app的图标的字典 24 */ 25 @property (nonatomic, strong) NSMutableDictionary *images; 26 @end 27 28 @implementation HMAppViewController 29 30 #pragma mark - 懒加载 31 - (NSArray *)apps 32 { 33 if(!_apps) 34 { 35 _apps = [HMApp apps]; 36 } 37 return _apps; 38 } 39 40 - (NSMutableDictionary *)operations 41 { 42 if(!_operations) 43 { 44 _operations = [NSMutableDictionary dictionary]; 45 } 46 return _operations; 47 } 48 49 - (NSOperationQueue *)queue 50 { 51 if(!_queue) 52 { 53 _queue = [[NSOperationQueue alloc] init]; 54 } 55 return _queue; 56 } 57 58 - (NSMutableDictionary *)images 59 { 60 if(!_images) 61 { 62 _images = [NSMutableDictionary dictionary]; 63 } 64 return _images; 65 } 66 67 - (void)viewDidLoad 68 { 69 [super viewDidLoad]; 70 71 } 72 73 - (void)didReceiveMemoryWarning 74 { 75 [super didReceiveMemoryWarning]; 76 77 // 移除所有的下载操作 78 [self.queue cancelAllOperations]; 79 [self.operations removeAllObjects]; 80 81 // 移除所有的图片缓存 82 [self.images removeAllObjects]; 83 } 84 85 // 默认是一组 86 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 87 { 88 return 1; 89 } 90 91 // 每一section有多少行 92 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 93 { 94 return self.apps.count; 95 } 96 97 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 98 { 99 static NSString *ID = @"app"; 100 UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID]; 101 if(!cell) 102 { 103 cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID]; 104 } 105 HMApp *app = self.apps[indexPath.row]; 106 cell.textLabel.text = app.name; 107 cell.detailTextLabel.text = app.download; 108 109 // 先从images缓存中取出图片url对应的UIImage 110 // 取出url对应的图片 111 UIImage *storageImage = self.images[app.icon]; 112 113 if(storageImage) // 说明图片已下载成功过(成功缓存)在内存中 114 { 115 cell.imageView.image = storageImage; 116 } 117 else 118 { 119 // 获取caches的路径,拼接文件路径 120 // NSString *file = HMAppImageFile(app.icon); 121 122 // 先从沙盒中取出图片 123 NSData *data = [NSData dataWithContentsOfFile:HMAppImageFile(app.icon)]; 124 125 if(data) 126 { // 如果沙盒中有值 127 cell.imageView.image = [UIImage imageWithData:data]; 128 } 129 else 130 { // 如果沙盒中没有值 131 // 要么显示已下载好的图片,要么显示占位图片,防止cell的循环利用时显示其它的图片 132 // 占位图,确保image有尺寸,更新时可以显示最新的图片 133 cell.imageView.image = [UIImage imageNamed:@"placeholder"]; 134 135 [self download:app.icon indexPath:indexPath]; 136 } 137 } 138 139 return cell; 140 141 // 怎么保证一图片(url)不被重复下载 142 // 用字典解决,key-》url,value-》image 143 } 144 145 /** 146 * 下载图片和刷新UI 147 */ 148 - (void)download:(NSString *)imageUrl indexPath:(NSIndexPath *)indexPath 149 { 150 NSBlockOperation *operation = self.operations[imageUrl]; 151 // NSInvocationOperation 152 153 if(operation) return; 154 155 // 解决循环强引用 156 __weak typeof(self) appVc = self; 157 158 operation = [NSBlockOperation blockOperationWithBlock:^{ 159 // [NSThread sleepForTimeInterval:2]; 160 161 // 创建操作,下载app图标,下载图片是耗时的操作 162 NSURL *url = [NSURL URLWithString:imageUrl]; 163 NSData *data = [NSData dataWithContentsOfURL:url]; // 下载图片 164 UIImage *image = [UIImage imageWithData:data]; // data->image 165 166 // 回到主线程刷新UI 167 [[NSOperationQueue mainQueue] addOperationWithBlock:^{ 168 // 设置cell图片(在子线程中的cell有可能给循环利用了,这里设置数据不合理) 169 // cell.imageView.image = image; 170 171 // 字典不能存放nil(空)的 172 if(image) 173 { 174 appVc.images[imageUrl] = image; 175 176 #warning 将图片存入沙盒中 177 // UIImage -> NSData -> File(文件) 178 NSData *data = UIImagePNGRepresentation(image); 179 180 // 获取caches的路径,拼接文件路径 181 // NSString *file = HMAppImageFile(imageUrl); 182 183 [data writeToFile:HMAppImageFile(imageUrl) atomically:YES]; 184 } 185 186 187 // 从字典中移除下载操作 188 [appVc.operations removeObjectForKey:imageUrl]; 189 190 // 刷新表格(刷新cell所在的行) 191 // 这里会调用(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 192 [appVc.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone]; 193 }]; 194 }]; 195 196 // 添加操作到队列中 197 [self.queue addOperation:operation]; 198 199 // 添加到字典中(这句代码为了解决重复下载) 200 self.operations[imageUrl] = operation; 201 } 202 203 // 用户开始拖拽表格时调用 204 -(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView 205 { 206 // 暂停下载 207 [self.queue setSuspended:YES]; 208 } 209 210 // 用户停止拖拽表格时调用 211 - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate 212 { 213 // 恢复下载 214 [self.queue setSuspended:NO]; 215 }