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.重复下载(设置操作缓存)
      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函数自己的创建的并发队列一起使用的时候才有效
posted @ 2016-01-12 16:41  __Qun  阅读(179)  评论(0编辑  收藏  举报