zoukankan      html  css  js  c++  java
  • 多线程- NSOperation

    ---恢复内容开始---

    lNSOperation和NSOperationQueue实现多线程的具体步骤
    先将需要执行的操作封装到一个NSOperation对象中
    然后将NSOperation对象添加到NSOperationQueue中
    系统会自动将NSOperationQueue中的NSOperation取出来
    将取出的NSOperation封装的操作放到一条新线程中执行

    注意:NSOperation是一个抽象的类,通过它的三个子类来封装任务 NSIvocationOperation NSBlockOperation 自定义NSOperation的方式
    @implementation ViewController
    
    -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
    {
        NSLog(@"-----");
        [self customOperation];
    }
    
    -(void)invocationOperation
    {
        //1.封装操作
        /*
         第一个参数:目标对象 self
         第二个参数:调用方法
         第三个参数:调用方法需要传递的参数
         */  通过NSIvocationOPeration封装操作
        NSInvocationOperation *op1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download1) object:nil];
        NSInvocationOperation *op2 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download1) object:nil];
        
        //2.启动操作 这里必须要启动操作
        [op1 start];
        [op2 start];
    }
    
    -(void)blockOperation
    {
        //1.封装操作  通过NSBlockOperation封装操作
        NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
            //主线程执行
            NSLog(@"download1---%@",[NSThread currentThread]);
                }];
        
        NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"download2---%@",[NSThread currentThread]);
        }];
        
        NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"download3---%@",[NSThread currentThread]);
        }];
        
        //追加任务 
        //追加的任务在子线程中并发执行意味着会开线程
        [op3 addExecutionBlock:^{
            NSLog(@"download4---%@",[NSThread currentThread]);
        }];
        
        [op3 addExecutionBlock:^{
            NSLog(@"download5---%@",[NSThread currentThread]);
        }];
        
        [op3 addExecutionBlock:^{
            NSLog(@"download6---%@",[NSThread currentThread]);
        }];
        
        //2.开始执行
        [op1 start];
        [op2 start];
        [op3 start];
    }
    
    -(void)customOperation
    {
        //1.封装任务  自定义NSOperation的方式封装任务也就是创建一个继承与NSOperation的类 重写-main方法,在方法中写要执行的任务
        /*内部会调用MSHOperation的main方法*/
        MSHOperation *op1 = [[MSHOperation alloc]init];
        MSHOperation *op2 = [[MSHOperation alloc]init];
        
        //2.开始执行
        [op1 start];
        [op2 start];
    }
    
    -(void)download1
    {
        NSLog(@"download1---%@",[NSThread currentThread]);
    }
    以上直接封装操作,没什么作用,只有将封装的操作NAOperation对象放进NSOperationQueue才会创建线程

    注意:默认异步,并发执行的执行的,如果想在控制它像GCD中的串行执行人任务的话,通过队列的最大并发数来设置(

    queue.maxConcurrentOperationCount = 1;

    )

    接下是将封装的操作放进NSOperationqueu中

    @implementation ViewController
    
    -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
    {
        NSLog(@"-----");
        [self customOperationWithQueue];
    }
    
    //GCD
    /*
     01 串行队列    自己创建|主队列
     02 并发队列    自己创建|全局并发队列
     */
    
    //NSOperation
    /* 获取队列的两种方式
     01 主队列 同GCD mainQueue  刷新UI
     02 非主队列 alloc init 特点:同时具备了串行和并发的功能|默认是并发的
     */
    -(void)invocationOperationWithQueue
    {
        //0.创建队列
        NSOperationQueue *queue = [[NSOperationQueue alloc]init];
        
        //1.封装操作
        /*
         第一个参数:目标对象 self
         第二个参数:调用方法
         第三个参数:调用方法需要传递的参数
         */
        NSInvocationOperation *op1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download1) object:nil];
        NSInvocationOperation *op2 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download2) object:nil];
        
        //2.启动操作
        //[op1 start]; 将NSOperation封装到队列中,不需要调用该方法,内部默认会执行该方法
        //[op2 start];
        
        //2.把操作添加到队列  在队列中添加多个NSOperation
        [queue addOperation:op1]; //[op1 start]
        [queue addOperation:op2]; //[op2 start]
    }
    
    -(void)blockOperationWithQueue
    {
        //0.创建队列
        NSOperationQueue *queue = [[NSOperationQueue alloc]init];
        
        //1.封装操作
        NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
            //主线程执行
            NSLog(@"download1---%@",[NSThread currentThread]);
        }];
        
        NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"download2---%@",[NSThread currentThread]);
        }];
        
        NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"download3---%@",[NSThread currentThread]);
        }];
        
        //追加任务
        //追加的任务在子线程中并发执行
        [op3 addExecutionBlock:^{
            NSLog(@"download4---%@",[NSThread currentThread]);
        }];
        
        [op3 addExecutionBlock:^{
            NSLog(@"download5---%@",[NSThread currentThread]);
        }];
        
        [op3 addExecutionBlock:^{
            NSLog(@"download6---%@",[NSThread currentThread]);
        }];
        
        //2.开始执行
    //    [op1 start]; 将封装操作的NSBlockOperation对象添加到队列中,内部也会调用- start方法
    //    [op2 start]; 
    //    [op3 start];
        
        [queue addOperation:op1];
        [queue addOperation:op2];
        [queue addOperation:op3];
        
    开方法常用简便的方法,下面是它的原理
    //简便方法:该方法内部会自动将block块里面的任务封装为一个NSBlockOperation对象,然后添加到队列 [queue addOperationWithBlock:^{ NSLog(@"download7---%@",[NSThread currentThread]); }]; } -(void)customOperationWithQueue { //0.创建队列 NSOperationQueue *queue = [[NSOperationQueue alloc]init]; //1.封装任务 /*内部会调用MSHOperation的main方法*/ MSHOperation *op1 = [[MSHOperation alloc]init]; MSHOperation *op2 = [[MSHOperation alloc]init]; //2.开始执行 //[op1 start]; //main 自定义的NSOperation内部会调用Start方法,并且也会调用它的-main方法 //[op2 start]; [queue addOperation:op1]; [queue addOperation:op2]; }

    下面是NSOperationqueu的一些常用属性的使用

    最大并发数
    同时执行的任务数
    比如,同时开3个线程执行3个任务,并发数就是3
    
    最大并发数的相关方法
    - (NSInteger)maxConcurrentOperationCount;
    - (void)setMaxConcurrentOperationCount:(NSInteger)cnt;
    
    队列的取消、暂停、恢复
    
    - (void)cancelAllOperations;
    提示:也可以调用NSOperation的- (void)cancel方法取消单个操作
    
    暂停和恢复队列
    - (void)setSuspended:(BOOL)b; // YES代表暂停队列,NO代表恢复队列
    - (BOOL)isSuspended;
    
    操作依赖
    
    NSOperation之间可以设置依赖来保证执行顺序
    比如一定要让操作A执行完后,才能执行操作B,可以这么写
    [operationB addDependency:operationA]; // 操作B依赖于操作A
    
    可以在不同queue的NSOperation之间创建依赖关系

    注意:不能相互依赖

    
    
    p比如A依赖B,B依赖A
     

      监听操作完成

    -(void)maxCount
    {
        //0.创建队列
        NSOperationQueue *queue = [[NSOperationQueue alloc]init];
        
        //设置最大并发数
        //-1 表示不受限制
        queue.maxConcurrentOperationCount = 0;
        
        //1.封装操作
        NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
            //主线程执行
            NSLog(@"download1---%@",[NSThread currentThread]);
        }];
        
        NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"download2---%@",[NSThread currentThread]);
        }];
        
        NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"download3---%@",[NSThread currentThread]);
            这里监听的是op3对象的任务完成
    [op3 setCompletionBlock:
    ^{ NSLog(@"download3下载完成"); }]; }]; [queue addOperation:op1]; [queue addOperation:op2]; [queue addOperation:op3]; }

    NSOperation线程之间的通信

    -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
    {
        //0.创建队列
        NSOperationQueue *queue = [[NSOperationQueue alloc]init];
        
        //1.封装操作
        NSBlockOperation *download = [NSBlockOperation blockOperationWithBlock:^{
           
            NSURL *url = [NSURL URLWithString:@"http://static.fever38.com/hotpolls/option_pic/2013032019073965331_500X.png"];
            NSData *data = [NSData dataWithContentsOfURL:url];
            UIImage *image = [UIImage imageWithData:data];
            NSLog(@"download--%@",[NSThread currentThread]);
            
            //回到主线程刷洗UI
            
         [[NSOperationQueue mainQueue] addOperationWithBlock:
    ^{ self.imageView.image = image; NSLog(@"UI--%@",[NSThread currentThread]); }]; }]; //2.添加操作到队列 [queue addOperation:download]; }

    多图下载的案例

    @interface ViewController ()
    /** tableView的数据源*/
    @property (nonatomic ,strong)NSArray *apps;
    /** 内存缓存*/
    @property (nonatomic ,strong)NSMutableDictionary *images;
    @property (nonatomic ,strong) NSOperationQueue *queue;
    
    /**  操作缓存*/
    @property (nonatomic ,strong)NSMutableDictionary *operations;
    @end
    
    @implementation ViewController
    
    #pragma mark ----------------------
    #pragma mark Lazy loading
    
    -(NSMutableDictionary *)operations
    {
        if (_operations == nil) {
            _operations = [NSMutableDictionary dictionary];
        }
        return _operations;
    }
    -(NSOperationQueue *)queue
    {
        if (_queue == nil) {
            _queue = [[NSOperationQueue alloc]init];
            _queue.maxConcurrentOperationCount = 6;
        }
        return _queue;
    }
    -(NSMutableDictionary *)images
    {
        if (_images == nil) {
            _images = [NSMutableDictionary dictionary];
        }
        return _images;
    }
    -(NSArray *)apps
    {
        if (_apps == nil) {
            
            //加载数据
            NSArray *arrayM = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle]pathForResource:@"apps.plist" ofType:nil]];
            
            //字典转模型  字典数组--->模型数组
            NSMutableArray *arrM = [NSMutableArray arrayWithCapacity:arrayM.count];
            for (NSDictionary *dict in arrayM) {
                [arrM addObject:[MSHApp appWithDict:dict]];
            }
            
            _apps = arrM;
            NSLog(@"%@",arrM);
        }
        
        return _apps;
    }
    #pragma mark ----------------------
    #pragma mark UITablViewDataSource
    -(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
    {
        return 1;
    }
    
    -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    {
        return self.apps.count;
    }
    
    -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        //1.确定标识符
        NSString *ID = @"app";
        
        //2.创建cell
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
        
        //3.设置cell
        
        //拿到该行cell对应的数据模型
        MSHApp *appM = self.apps[indexPath.row];
        
        //设置标题和子标题
        cell.textLabel.text = appM.name;
        cell.detailTextLabel.text = appM.download;
        
      // 加载图片先根据key去缓存中去取 UIImage *image = [self.images objectForKey:appM.icon]; if (image) { cell.imageView.image = image; }else {   如果缓存区中没有,则去沙盒中去取 //现尝试去沙河中取,如果没有那么才下载 NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]; NSString *fileName = [appM.icon lastPathComponent]; //文件的全路径 NSString *fullPth = [caches stringByAppendingPathComponent:fileName]; //NSLog(@"%@",fullPth); NSData *data = [NSData dataWithContentsOfFile:fullPth]; //沙盒缓存清空 data = nil; if (data) { UIImage *image = [UIImage imageWithData:data]; cell.imageView.image = image; //保存图片到内存缓存 注意:这里要记得从沙盒中获取图片以后,要添加到缓存区中 [self.images setObject:image forKey:appM.icon]; NSLog(@"沙河中有这样图片了"); }else {
    如果沙盒中也没有,先设置站位图片,在根据URL区下载照片 //设置展占位图片 cell.imageView.image = [UIImage imageNamed:@"Snip20151222_498"]; //查看该图片的下载操作是否存在 线程是并发执行的,先判断有没有下载操作存在 NSBlockOperation *download = [self.operations objectForKey:appM.icon]; if (download == nil) { download = [NSBlockOperation blockOperationWithBlock:^{ //显示图片 NSURL *url = [NSURL URLWithString:appM.icon]; [NSThread sleepForTimeInterval:1.0]; NSData *data = [NSData dataWithContentsOfURL:url]; UIImage *image = [UIImage imageWithData:data]; NSLog(@"-下载图片操作---%zd",indexPath.row); if (image == nil) { [self.operations removeObjectForKey:appM.icon]; return ; } //保存图片到内存缓存 [self.images setObject:image forKey:appM.icon]; //保存图片到沙河缓存 [data writeToFile:fullPth atomically:YES]; //线程间通信 [[NSOperationQueue mainQueue]addOperationWithBlock:^{ // NSLog(@"----%zd",indexPath.row); // cell.imageView.image = image; //刷新某一行 [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone]; }]; [self.operations removeObjectForKey:appM.icon]; }]; //加入到操作缓存 [self.operations setObject:download forKey:appM.icon]; //把操作添加到队列 [self.queue addOperation:download]; } } } //4.返回cell return cell; } /* Documents:会备份,不能把下载的数据保存在这个文件下面 Libriary caches preferences:偏好设置 tmp:临时文件夹 */ //问题 //01 UI不流畅 ------>开子线程下载 //01 数据不显示 //02 图片重复下载 ---操作缓存 //02 重复下载 ----内存缓存|优化(沙河缓存)

    多图下载的案例(通过SDWebImageView)

    框架下载地址 https://github.com/rs/SDWebImage

     
    #import "ViewController.h"
    #import "MSHApp.h" 自定义模型
    #import "UIImageView+WebCache.h" 包含的框架的头文件
    
    @interface ViewController ()
    /** tableView的数据源*/
    @property (nonatomic ,strong)NSArray *apps;
    /** 内存缓存*/
    @property (nonatomic ,strong)NSMutableDictionary *images; 缓存
    @property (nonatomic ,strong) NSOperationQueue *queue; 存储操作
    
    /**  操作缓存*/
    @property (nonatomic ,strong)NSMutableDictionary *operations;
    @end
    
    @implementation ViewController
    
    #pragma mark ----------------------
    #pragma mark Lazy loading
    
    -(NSMutableDictionary *)operations
    {
        if (_operations == nil) {
            _operations = [NSMutableDictionary dictionary];
        }
        return _operations;
    }
    -(NSOperationQueue *)queue
    {
        if (_queue == nil) {
            _queue = [[NSOperationQueue alloc]init];
            _queue.maxConcurrentOperationCount = 6;
        }
        return _queue;
    }
    -(NSMutableDictionary *)images
    {
        if (_images == nil) {
            _images = [NSMutableDictionary dictionary];
        }
        return _images;
    }
    -(NSArray *)apps
    {
        if (_apps == nil) {
            
            //加载数据
            NSArray *arrayM = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle]pathForResource:@"apps.plist" ofType:nil]];
            
            //字典转模型  字典数组--->模型数组
            NSMutableArray *arrM = [NSMutableArray arrayWithCapacity:arrayM.count];
            for (NSDictionary *dict in arrayM) {
                [arrM addObject:[MSHApp appWithDict:dict]];
            }
            
            _apps = arrM;
            NSLog(@"%@",arrM);
        }
        
        return _apps;
    }
    #pragma mark ----------------------
    #pragma mark UITablViewDataSource
    -(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
    {
        return 1;
    }
    
    -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    {
        return self.apps.count;
    }
    
    -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        //1.确定标识符
        NSString *ID = @"app";
        
        //2.创建cell
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
        
        //3.设置cell
        
        //拿到该行cell对应的数据模型
        MSHApp *appM = self.apps[indexPath.row];
        
        //设置标题和子标题
        cell.textLabel.text = appM.name;
        cell.detailTextLabel.text = appM.download;
    这里这需要一句话,解决上面的所有逻辑 牛逼!!!!吧
    第一个参数是图片的URL

    第二个图片是站位图片
    [cell.imageView sd_setImageWithURL:[NSURL URLWithString:appM.icon] placeholderImage:[UIImage imageNamed:
    @"Snip20151222_498"]]; NSLog(@"%@",[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]); //4.返回cell return cell; }

    ---恢复内容结束---

  • 相关阅读:
    CCCC练习即感
    1003 我能通过
    录制开讲啦杂感
    OOP第三次上机
    关于C++随机函数
    蓝桥杯杂感。
    CF502C The Phone Number
    It's a secret
    2017-06-22
    2017-05-12
  • 原文地址:https://www.cnblogs.com/mshong1616/p/5096453.html
Copyright © 2011-2022 走看看