标签:
内存指的就是主板上的存储部件,是CPU直接与之沟通,并用其存储数据的部件,存放当前正在使用的(即执行中)的数据和程序,它的物理实质就是一组或多组具备数据输入输出和数据存储功能的集成电路,内存只用于暂时存放程序和数据,一旦关闭电源或发生断电,其中的程序和数据就会丢失。
1 #import "ViewController.h" 2 #import "CZApp.h" 3 @interface ViewController () 4 // plist文件数据的容器 5 @property (nonatomic, strong) NSArray *appList; 6 7 // 管理下载的全局队列 8 @property (nonatomic, strong) NSOperationQueue *opQueue; 9 10 /**所有下载的缓冲池*/ 11 @property (nonatomic, strong) NSMutableDictionary *operationCache; 12 13 /**保存所有图像的缓存*/ 14 @property (nonatomic, strong) NSMutableDictionary *imageCache; 15 @end 16 17 18 @implementation ViewController 19 20 // 懒加载 21 -(NSArray *)appList 22 { 23 if (_appList == nil) { 24 NSArray *dicArray = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"apps.plist" ofType:nil]]; 25 // 字典转模型 26 NSMutableArray *arryM = [NSMutableArray array]; 27 for(NSDictionary *dict in dicArray){ 28 CZApp *app = [CZApp appWithDict:dict]; 29 [arryM addObject:app]; 30 } 31 _appList = arryM; 32 } 33 return _appList; 34 } 35 36 -(NSOperationQueue *)opQueue 37 { 38 if (_opQueue == nil) { 39 _opQueue = [[NSOperationQueue alloc] init]; 40 } 41 return _opQueue; 42 } 43 44 -(NSMutableDictionary *)operationCache 45 { 46 if (_operationCache == nil) { 47 _operationCache = [[NSMutableDictionary alloc] init]; 48 } 49 return _operationCache; 50 } 51 52 -(NSMutableDictionary *)imageCache 53 { 54 if (_imageCache == nil) { 55 _imageCache = [[NSMutableDictionary alloc] init]; 56 } 57 return _imageCache; 58 } 59 60 #pragma mark - 实现数据源方法 61 -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 62 { 63 return self.appList.count; 64 } 65 66 /** 67 - 重构代码,便于维护问题1:如果网速比较慢,会很卡 68 解决方法:使用异步下载 69 70 问题2:图片没有Frame,所有cell初始化的时候,给imageView的frame是0。异步下载完之后不显示 解决办法:使用占位图(如果展位图比较大, 自定义cell可以解决) 71 72 问题3:如果图片下载速度不一致,同时用户快速滚动的时候,会因为Cell的重用导致图片混乱 73 解决方法:MVC,使用Model(模型)保存下载的图像,再次刷新表格。 74 75 问题4:在用户快速滚动的时候,会重复添加下载任务到下载队列。 76 解决方法:建立下载操作的缓冲池。首先检查缓冲池里是否有当前图片的下载操作。有的话就不创建下载操作。从而保证一张图片只添加一个下载操作。其实就是建立一个全局的字典属性。 77 78 问题5: 将图片保存到模型里的优缺点 79 优点:不用重复下载,利用MVC刷新表格,不会造成数据混乱,加载速度比较快 80 缺点:内存,所有下载好的图像都会记录在模型里。如果数据比较多(2000)个就会造成内存警告。 81 82 -***图像根模型耦合性太强。导致清理内存非常困难 83 解决办法:模型跟图像分开。在控制器里做缓存。 84 问题6:下载操作缓冲池会越来越大。需要及时清理。 85 86 */ 87 /** 88 代码重构:1.如果代码太长。 89 目的: 90 - 写的时候,如果思路清楚,能够一次性写完,但是也要注意同构。 91 - 时间长了,不好阅读 92 93 94 重构方法: 95 如果有一部分代码专门解决某一问题,就封装起来。 96 1. 新建一个方法—> 剪切代码。 97 2. 传参数。 98 3. 在原来剪切代码的地方,调用抽取的方法。 99 4. 注意,测试。 100 5. 注意if嵌套,在实际的开发,非常忌讳很深的嵌套。 101 */ 102 103 -(void)viewDidLoad 104 { 105 NSLog(@"%@", [self cachePathWithUrl:@""]); 106 } 107 108 // cell里面的imageView子控件是懒加载 109 -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 110 { 111 static NSString *ID = @"AppCell"; 112 UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:ID]; 113 // 给Cell设置数据 114 CZApp *app = self.appList[indexPath.row]; 115 cell.textLabel.text = app.name; 116 cell.detailTextLabel.text = app.download; 117 118 // 判断模型里面是否有图像 119 if ([self.imageCache objectForKey:app.icon]) { // 内存有图片 120 NSLog(@" 图片已经下载......"); 121 cell.imageView.image = self.imageCache[app.icon]; 122 }else{ 123 // 内存无图片 124 // 显示图片 125 // 如果沙盒里面有图片,直接从沙盒加载 126 UIImage *image = [UIImage imageWithContentsOfFile:[self cachePathWithUrl:app.icon]]; 127 if (image) { // 沙盒有图片 128 NSLog(@"直接从沙盒加载图片"); 129 // 1. 设置图片缓存到内存,方便下次从内存直接加载 130 [self.imageCache setObject:image forKey:app.icon]; 131 132 // 2. 显示图片到cell 133 cell.imageView.image = self.imageCache[app.icon]; 134 }else{ // 沙盒没有图片 135 136 // 显示图片—占位图 137 cell.imageView.image = [UIImage imageNamed:@"user_default"]; 138 //#warning mark-从这里开始剪切的代码 139 140 // 下载图片 141 [self downloadImage:indexPath]; 142 } 143 } 144 return cell; 145 } 146 147 -(void)downloadImage:(NSIndexPath *)indexPath 148 { 149 CZApp *app = self.appList[indexPath.row]; 150 /** 151 如果下载缓冲池里面有当前图片的下载操作,就不用创建下载操作,没有才创建 152 缓冲池字典中 key:存放当前图片的url,字符串类型。 153 Value:保存下载操作 154 */ 155 if (self.operationCache[app.icon]) 156 { 157 NSLog(@"正在玩命下载中......"); 158 return; 159 } 160 // 缓冲池没有下载操作 161 162 // 异步下载图片 163 __weak typeof(self) weakSelf = self; 164 NSBlockOperation *downLoadOp = [NSBlockOperation blockOperationWithBlock:^{ 165 // 模拟延时 166 [NSThread sleepForTimeInterval:2]; 167 NSLog(@"正在下载中......"); 168 169 // 1. 下载图片(二进制数据) 170 NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:app.icon]]; 171 NSLog(@"%@", data); 172 UIImage *image = [UIImage imageWithData:data]; 173 174 // 2. 将下载的数据保存到沙盒 175 // 字典的赋值不能为nil,赋值为nil会崩溃 176 if (image) { 177 // 先保存到图片缓存 178 [weakSelf.imageCache setObject:image forKey:app.icon]; 179 180 // 将图片写入到沙盒 181 [data writeToFile:[self cachePathWithUrl:app.icon] atomically:YES]; 182 } 183 184 // 3 将操作从缓冲池删除——将下载好的图片操作从缓冲池中移除 185 [weakSelf.operationCache removeObjectForKey:app.icon]; 186 187 // 4. 在主线程更新UI 188 [[NSOperationQueue mainQueue] addOperationWithBlock:^{ 189 [weakSelf.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone]; 190 /** reload 会重新调用cell的初始化方法, 会重新判断模型里面是否有图像 191 有的话就直接显示 192 */ 193 }]; 194 }]; 195 196 // 将操作添加到队列 197 [weakSelf.opQueue addOperation:downLoadOp]; 198 NSLog(@"操作的数量------------->%ld", self.opQueue.operationCount); 199 200 // 将操作添加到缓冲池中(使用图片的url作为key) 201 [weakSelf.operationCache setObject:downLoadOp forKey:app.icon]; 202 } 203 204 -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 205 { // 点击后查看操作缓冲池内的操作详情 206 NSLog(@"%@", self.operationCache); 207 } 208 209 /** 210 在真实开发中,一定要注意这个方法 211 */ 212 -(void)didReceiveMemoryWarning 213 { 214 [super didReceiveMemoryWarning]; 215 216 // 需要在这里做一些内存清理的工作,如果不处理会被系统强制闪退 217 // 清理内存 218 [self.imageCache removeAllObjects]; 219 220 // 清理操作的缓存 221 [self.operationCache removeAllObjects]; 222 223 // 取消下载队列内的任务 224 [self.opQueue cancelAllOperations]; 225 } 226 227 /** 228 拼接一个文件在沙盒的全路径 229 */ 230 -(NSString *)cachePathWithUrl:(NSString *)urlStr 231 { // 将图片网址名作为作为最后一项 232 // 1. 获得缓存的路径 233 NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]; 234 235 // 2. 把路径根urlStr拼接起来 236 return [cachePath stringByAppendingPathComponent:urlStr.lastPathComponent]; 237 } 238 -(void)dealloc 239 { 240 NSLog(@"销毁控制器-------------"); 241 } 242 @end
有沙盒路径对我们比较重要,因为他能够让我们在断网且退出程序后,在进入程序任然能够使用。当然我们也必须限制沙盒中存入数据的个数,最好能让其更新。这样我们就无需担心没有网时只能看见很久以前的消息。
下一篇会说如何更新缓存。
标签:
原文地址:http://www.cnblogs.com/YU411524/p/4929585.html