NSOperation 之 Block循环引用——以及nil Nil NULL [NULL null]的区别
来源:互联网 发布:生产跟单软件 编辑:程序博客网 时间:2024/06/06 10:42
[NSNull null]; 空对象,可以存放到字典或数组。
NULL: c语言的空指针
nil: OC指向空对象的指针
Nil: 空类。
根控制器永远不会销毁。所以要新建控制器进行验证。
任何对象放到集合中都是强引用。
必须注意避免block的循环引用,对外部变量的强引用
- self 要小心
- 借助dealloc方法 判断是否循环引用
- 只要是在block块内需要应用外部控制器时,就在外部声明弱引用如下:
__weak typeof(self) weakSelf = self;
在原来代码的基础之上,稍作修改,进行验证Block的循环引用。步骤如下:
运行后点击Item 进入页面控制器,然后点击返回,后台结果如下:
当我们把[self.operationCacheremoveObjectForKey:app.icon]; 这句让当前操作从缓存池中移除的代码屏蔽后,运行如下:
发现控制器没有被销毁。原理如下:当屏蔽从操作缓冲池中移除当前操作的代码后。由于当前控制器对操作缓冲池进行的是强引用,而缓冲池是一个集合对内部存的当前操作即Block块也是强引用,而再Block块内又使用self对当前控制器进行了一个强引用。如下图所示,造成了Block循环引用。
而没有屏蔽从操作缓冲池移除当前操作之前,等于说下载完成后把当前操作即Block块从下载操作的缓冲池中移除。切断了循环引用。所以能销毁控制器。但是为了安全起见还是在Block块内把用到当前控制器的地方的self声明为弱引用,即在Block外部声明__weak typeof(self) weakSelf = self;在Block块内将self替换为weakSelf即可。改善后的代码如下所示:
CZApp.h
<span style="font-size:18px;">//// CZApp.h// NSoperation之网络图片下载//// Created by apple on 15/10/23.// Copyright (c) 2015年 LiuXun. All rights reserved.//#import <UIKit/UIKit.h>@interface CZApp : NSObject@property(nonatomic, copy) NSString *name;@property (nonatomic, copy) NSString *icon;@property (nonatomic, strong) NSString *download;/** 保存网络下载的图像 */// @property(nonatomic, strong) UIImage *image;+(instancetype) appWithDict:(NSDictionary *) dict;@end</span>CZApp.m
<span style="font-size:18px;">//// CZApp.m// NSoperation之网络图片下载//// Created by apple on 15/10/23.// Copyright (c) 2015年 LiuXun. All rights reserved.//#import "CZApp.h"@implementation CZApp+(instancetype) appWithDict:(NSDictionary *) dict{ CZApp *app = [[self alloc] init]; [app setValuesForKeysWithDictionary:dict]; return app;}@end</span>viewController.m
<span style="font-size:18px;">//// ViewController.m// NSoperation之网络图片下载//// Created by apple on 15/10/23.// Copyright (c) 2015年 LiuXun. All rights reserved.//#import "ViewController.h"#import "CZApp.h"@interface ViewController ()// plist文件数据的容器@property (nonatomic, strong) NSArray *appList;// 管理下载的全局队列@property (nonatomic, strong) NSOperationQueue *opQueue;/**所有下载的缓冲池*/@property (nonatomic, strong) NSMutableDictionary *operationCache;/**保存所有图像的缓存*/@property (nonatomic, strong) NSMutableDictionary *imageCache;@end@implementation ViewController// 懒加载-(NSArray *)appList{ if (_appList == nil) { NSArray *dicArray = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"apps.plist" ofType:nil]]; // 字典转模型 NSMutableArray *arryM = [NSMutableArray array]; for(NSDictionary *dict in dicArray){ CZApp *app = [CZApp appWithDict:dict]; [arryM addObject:app]; } _appList = arryM; } return _appList;}-(NSOperationQueue *)opQueue{ if (_opQueue == nil) { _opQueue = [[NSOperationQueue alloc] init]; } return _opQueue;}-(NSMutableDictionary *)operationCache{ if (_operationCache == nil) { _operationCache = [[NSMutableDictionary alloc] init]; } return _operationCache;}-(NSMutableDictionary *)imageCache{ if (_imageCache == nil) { _imageCache = [[NSMutableDictionary alloc] init]; } return _imageCache;}#pragma mark - 实现数据源方法-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ return self.appList.count;}/** 问题1:如果网速比较慢,会很卡 解决方法:使用异步下载 问题2:图片没有Frame,所有cell初始化的时候,给imageView的frame是0。异步下载完之后不显示 解决办法:使用占位图(如果展位图比较大, 自定义cell可以解决) 问题3:如果图片下载速度不一致,同时用户快速滚动的时候,会因为Cell的重用导致图片混乱 解决方法:MVC,使用Model(模型)保存下载的图像,再次刷新表格。 问题4:在用户快速滚动的时候,会重复添加下载任务到下载队列。 解决方法:建立下载操作的缓冲池。首先检查缓冲池里是否有当前图片的下载操作。有的话就不创建下载操作。从而保证一张图片只添加一个下载操作。其实就是建立一个全局的字典属性。 问题5: 将图片保存到模型里的优缺点 优点:不用重复下载,利用MVC刷新表格,不会造成数据混乱,加载速度比较快 缺点:内存,所有下载好的图像都会记录在模型里。如果数据比较多(2000)个就会造成内存警告。 -***图像根模型耦合性太强。导致清理内存非常困难 解决办法:模型跟图像分开。在控制器里做缓存。 问题6:下载操作缓冲池会越来越大。需要及时清理。 *//** 代码重构:1.如果代码太长。 目的: - 写的时候,如果思路清楚,能够一次性写完,但是也要注意同构。 - 时间长了,不好阅读 - 重构代码,便于维护 重构方法: 如果有一部分代码专门解决某一问题,就封装起来。 1. 新建一个方法—> 剪切代码。 2. 传参数。 3. 在原来剪切代码的地方,调用抽取的方法。 4. 注意,测试。 5. 注意if嵌套,在实际的开发,非常忌讳很深的嵌套。 */// cell里面的imageView子控件是懒加载-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ static NSString *ID = @"AppCell"; UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:ID]; // 给Cell设置数据 CZApp *app = self.appList[indexPath.row]; cell.textLabel.text = app.name; cell.detailTextLabel.text = app.download; // 判断模型里面是否有图像 if ([self.imageCache objectForKey:app.icon]) { NSLog(@" 图片已经下载......"); cell.imageView.image = self.imageCache[app.icon]; }else{ // 如果模型内没有图片就异步下载 // 显示图片—占位图 cell.imageView.image = [UIImage imageNamed:@"user_default"];#warning 从这里开始剪切的代码 // 下载图片 [self downloadImage:indexPath]; } return cell;}-(void)downloadImage:(NSIndexPath *)indexPath{ CZApp *app = self.appList[indexPath.row]; /** 如果下载缓冲池里面有当前图片的下载操作,就不用创建下载操作,没有才创建 缓冲池字典中 key:存放当前图片的url,字符串类型。 Value:保存下载操作 */ if (self.operationCache[app.icon]) { NSLog(@"正在玩命下载中......"); return; } // 缓冲池没有下载操作 // 异步下载图片 __weak typeof(self) weakSelf = self; NSBlockOperation *downLoadOp = [NSBlockOperation blockOperationWithBlock:^{ // 模拟延时 [NSThread sleepForTimeInterval:2]; NSLog(@"正在下载中......"); // 1. 下载图片(二进制数据) NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:app.icon]]; UIImage *image = [UIImage imageWithData:data]; // 2. 将下载的数据保存到模型 // 字典的赋值不能为nil,赋值为nil会崩溃 if (image) { [weakSelf.imageCache setObject:image forKey:app.icon]; // 3 将操作从缓冲池删除——将下载好的图片操作从缓冲池中移除 [weakSelf.operationCache removeObjectForKey:app.icon]; } // 4. 在主线程更新UI [[NSOperationQueue mainQueue] addOperationWithBlock:^{ [weakSelf.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone]; /** reload 会重新调用cell的初始化方法, 会重新判断模型里面是否有图像 有的话就直接显示 */ }]; }]; // 将操作添加到队列 [weakSelf.opQueue addOperation:downLoadOp]; NSLog(@"操作的数量------------->%ld", self.opQueue.operationCount); // 将操作添加到缓冲池中(使用图片的url作为key) [weakSelf.operationCache setObject:downLoadOp forKey:app.icon];}-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ // 点击后查看操作缓冲池内的操作详情 NSLog(@"%@", self.operationCache);}/** 在真实开发中,一定要注意这个方法 */-(void)didReceiveMemoryWarning{ [super didReceiveMemoryWarning]; // 需要在这里做一些内存清理的工作,如果不处理会被系统强制闪退 // 清理内存 [self.imageCache removeAllObjects]; // 清理操作的缓存 [self.operationCache removeAllObjects]; // 取消下载队列内的任务 [self.opQueue cancelAllOperations];}-(void)dealloc{ NSLog(@"销毁控制器-------------");}@end</span>
- NSOperation 之 Block循环引用——以及nil Nil NULL [NULL null]的区别
- null nil Nil 的区别
- null nil Nil 的区别
- nil,Nil,NULL的区别
- nil/Nil/NULL的区别
- nil null Nil 的区别
- nil,Nil,NULL的区别
- nil,Nil,NULL的区别
- iOS中nil/Nil/NULL的区别以及[NSNull null]
- nil NULL Nil 区别
- nil,Nil,Null区别
- nil,NULL,Nil区别?
- iOS之nil, Nil, NULL,null和NSNull的区别
- 浅谈iOS 中 nil、Nil、null以及NSNull的区别
- nil、NIL以及null间的联系和区别
- nil/Nil/NULL/NSNull的区别
- nil/Nil/NULL/NSNull的区别
- nil、Nil、NULL、NSNull 的区别
- 欢迎使用CSDN-markdown编辑器
- Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系
- Java——继承,抽象,接口
- 创建第一个android 应用程序时,SDK和ADT一定要保持版本一致。
- 推荐外链网盘 OpenDrive
- NSOperation 之 Block循环引用——以及nil Nil NULL [NULL null]的区别
- 指定长度递增子序列计数【CCPC C题】
- 欢迎使用CSDN-markdown编辑器
- 基于Python的江苏大学校园网暴力破解
- nyoj 82 迷宫寻宝(一)(广搜)
- js正则
- MySQL数据库的同步配置+MySql读写分离
- request.getParameter() 和request.getAttribute() 区别
- DOM要点总结