多线程- 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; }

 

 

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

posted @ 2016-01-03 15:13  mshong  阅读(251)  评论(0编辑  收藏  举报