iOS多线程-NSOperation简单语法
1> 封装操作
NSInvocationOperation
NSBlockOperation
2> NSOperationQueue
1 主队列 通过mainQueue获得,凡是放到主队列中的任务都将在主线程执行
2 非主队列 直接alloc init出来的队列。非主队列同时具备了并发和串行的功能,通过设置最大并发数属性来控制任务是并发执行还是串行执行,默认是并发
3> 步骤:
//封装操作
NSInvocationOperation *op1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download1) object:nil];
//创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
//把操作添加到队列中
[queue addOperation:op1];
4> 其他方法:
//1)封装操作2)把操作添加到队列中
[queue addOperationWithBlock:^{
NSLog(@"7----%@",[NSThreadcurrentThread]);
}];
//追加任务 如果内部的操作数量大于1,那么就会开子线程异步执行
[op3 addExecutionBlock:^{
NSLog(@"4----%@",[NSThread currentThread]);
}];
//最大并发数设置
- (void)setMaxConcurrentOperationCount:(NSInteger)cnt;
/*
暂停 suspended
suspended设置为YES表示暂停 暂停表示不继续执行队列中的下一个任务,暂停操作是可以恢复的
取消 cancelAllOperations
取消队列里面的所有操作,取消之后,当前正在执行的操作的下一个操作将不再执行,而且永远都不在执行,就像后面的所有任务都从队列里面移除了一样,取消操作是不可以恢复的
[self.queue cancelAllOperations];
*/
//自定义NSOperation(XMGOperation)取消操作 GQOperation下的main函数:(main函数是说明执行的是哪些操作)
-(void)main
{
//耗时操作1
for (int i = 0; i<1000; i++) {
NSLog(@"任务1-%d--%@",i,[NSThread currentThread]);
}
//苹果官方建议,每当执行完一次耗时操作之后,就查看一下当前队列是否为取消状态,如果是,那么就直接退出
//好处是可以提高程序的性能
if (self.isCancelled) {
return;
}
//耗时操作2
for (int i = 0; i<1000; i++) {
NSLog(@"任务1-%d--%@",i,[NSThread currentThread]);
}
}
//设置依赖 addDependency
[operationB addDependency:operationA]; // 操作B依赖于操作A 而且可以跨队列依赖
//completionBlock 某操作完成后再执行的操作
op4.completionBlock = ^{ //op4操作完才执行如下代码块
NSLog(@“---");
};
5> NSThread线程间通信
6> 多图下载问题
出现的问题:
1)重复下载(内存缓存)—>磁盘缓存
2)UI不流畅(开子线程去下载图片)
1.图片不显示(刷新指定行)
2.重复下载(设置操作缓存)
1.图片不显示(刷新指定行)
2.重复下载(设置操作缓存)
3.数据错乱(设置占位图片)
设置图片的逻辑:
//查看该图片在缓存中是否存在,如果存在那么就直接设置,否则查看沙盒是否存在
//如果存在,直接设置图片&保存一份到内存
//如果存在,直接设置图片&保存一份到内存
//如果不存在,就去下载,再保存到内存以及沙盒
(如果plist文件中图片的路径是错的话会导致程序崩溃,所以应该对获得的图片进行判断,如果图片为nil,那么直接return,但在return之前必须移除该图片的下载的队列,表示下载失败之后就不会再添加在下载队列中不再下载)
1 @interface ViewController () 2 //保存app模型数组 3 @property (nonatomic, strong) NSMutableArray *appArr; 4 //内存缓存 5 @property (nonatomic, strong) NSCache *imageCache; 6 //队列数组 7 @property (nonatomic, strong) NSMutableDictionary *operationDict; 8 //下载队列 9 @property (nonatomic, strong) NSOperationQueue *queue; 10 @end 11 @implementation ViewController 12 - (NSCache *)imageCache{ 13 if (_imageCache == nil) { 14 _imageCache = [NSCache alloc] init]; 15 _imageCache.countLimit = 10; //可以能缓存的最大数量 16 } 17 return _imageCache; 18 } 19 - (NSMutableDictionary *)operationDict{ 20 if (_operationDict == nil) { 21 _operationDict = [NSMutableDictionary dictionary]; 22 } 23 return _operationDict; 24 } 25 -(NSOperationQueue *)queue 26 { 27 if(_queue == nil) 28 { 29 _queue = [[NSOperationQueue alloc]init]; 30 _queue.maxConcurrentOperationCount = 5; 31 } 32 return _queue; 33 } 34 - (NSMutableArray *)appArr{ 35 if (_appArr == nil) { 36 NSMutableArray *models = [NSMutableArray array]; 37 NSString *str = [[NSBundle mainBundle] pathForResource:@"apps.plist" ofType:nil]; 38 NSArray *arrDict = [NSArray arrayWithContentsOfFile:str]; 39 for (NSDictionary *dict in arrDict) { 40 appItem *app = [appItem appItemWithDict:dict]; 41 [models addObject:app]; 42 } 43 _appArr = [models copy]; 44 } 45 return _appArr; 46 } 47 - (void)viewDidLoad { 48 [super viewDidLoad]; 49 // Do any additional setup after loading the view, typically from a nib. 50 } 51 52 53 #pragma mark - 数据源方法 54 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 55 return self.appArr.count; 56 } 57 58 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 59 static NSString *ID = @"app"; 60 UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID]; 61 62 appItem *app = self.appArr[indexPath.row]; 63 cell.textLabel.text = app.name; 64 cell.detailTextLabel.text = app.download; 65 66 //从内存中去 67 UIImage *image = [self.imageCache objectForKey:app.icon]; 68 if (image) { //如果图片存在内存中 69 cell.imageView.image = image; 70 NSLog(@"内存中取%i",indexPath.row); 71 }else{ 72 NSData *data = [NSData dataWithContentsOfFile:[self getFullPath:app.icon]]; 73 data = nil; 74 if (data) { //如果图片存在沙盒中 75 //沙盒的图片数据转换成图片 76 UIImage *image = [UIImage imageWithData:data]; 77 cell.imageView.image = image; 78 //保存一份到内存 79 [self.imageCache setObject:image forKey:app.icon]; 80 }else{ 81 //下载的时候还没下载完用占位图片显示 82 cell.imageView.image = [UIImage imageNamed:@"屏幕快照"]; 83 //取出操作 84 NSOperation *operation = [self.operationDict objectForKey:app.icon]; 85 if (operation) { //已有下载操作 86 //什么都不干 87 NSLog(@"%i已在下载", indexPath.row); 88 }else{ 89 NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ 90 [NSThread sleepForTimeInterval:2]; 91 NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:app.icon]]; 92 UIImage *image = [UIImage imageWithData:data]; 93 cell.imageView.image = image; 94 95 //如果图片的链接是错的情况 96 if (image == nil) { 97 //取消队列中的操作 98 [self.operationDict removeObjectForKey:app.icon]; 99 return; 100 } 101 102 //保存到内存中 103 [self.imageCache setObject:image forKey:app.icon]; 104 //保存到沙盒 105 NSString *fullPath = [self getFullPath:app.icon]; 106 //写入 107 [data writeToFile:fullPath atomically:YES]; 108 NSLog(@"下载%i",indexPath.row); 109 110 //回到主线程刷新ui 111 [[NSOperationQueue mainQueue] addOperationWithBlock:^{ 112 [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationTop]; 113 }]; 114 115 //下载完毕后要移除下载操作 116 [self.operationDict removeObjectForKey:app.icon]; 117 }]; 118 119 //保存操作进队列数组 120 [self.operationDict setObject:operation forKey:app.icon]; 121 [self.queue addOperation:operation]; 122 } 123 124 } 125 } 126 return cell; 127 } 128 //获得全路径 129 - (NSString *)getFullPath:(NSString *)urlName{ 130 NSString *cache = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).lastObject; 131 //获得文件名称 132 NSString *name = [urlName lastPathComponent]; 133 //拼接文件全路径 134 NSString *fullPath = [cache stringByAppendingPathComponent:name]; 135 return fullPath; 136 } 137 //收到内存警告 138 - (void)didReceiveMemoryWarning{ 139 //清空缓存 140 [self.imageCache removeAllObjects]; 141 [self.operationDict removeAllObjects]; 142 143 //清空正在下载的操作 144 [self.queue cancelAllOperations]; 145 } 146 @end
7> 计算代码段执行的时间
//第一种方法 NSDate
NSDate *start = [NSDate date];
NSData *data = [NSData dataWithContentsOfURL:url];
NSDate *end = [NSDate date];
NSLog(@"第二步操作花费的时间为%f",[end timeIntervalSinceDate:start]);
//第二种方法 CFTimeInterval
CFTimeInterval start = CFAbsoluteTimeGetCurrent();
NSData *data = [NSData dataWithContentsOfURL:url];
CFTimeInterval end = CFAbsoluteTimeGetCurrent();
NSLog(@"第二步操作花费的时间为%f",end - start);
8>使用Crearte函数创建的并发队列和全局并发队列的主要区别:
1.全局并发队列在整个应用程序中本身是默认存在的,并且对应有高优先级、默认优先级、低优先级和后台优先级一共四个并发队列,我们只是选择其中的一个直接拿来用。而Crearte函数是实打实的从头开始去创建一个队列。
2.在iOS6.0之前,在GCD中凡是使用了带Crearte和retain的函数在最后都需要做一次release操作。而主队列和全局并发队列不需要我们手动release。当然了,在iOS6.0之后GCD已经被纳入到了ARC的内存管理范畴中,即便是使用retain或者create函数创建的对象也不再需要开发人员手动释放,我们像对待普通OC对象一样对待GCD就OK。
3.在使用栅栏函数的时候,苹果官方明确规定栅栏函数只有在和使用create函数自己的创建的并发队列一起使用的时候才有效