澳门威利斯人_威利斯人娱乐「手机版」

来自 办公软件 2020-05-01 05:58 的文章
当前位置: 澳门威利斯人 > 办公软件 > 正文

实现自定义NSOperation,多线程之初识NSOperation

- main

第一,看见二十六线程大家会优先想起GCD.在此之前被提问.有了GCD为何还要封装NSOperation?相信百度是有大多答案,然则,在作者眼里NSOperation,新扩展的属性,操作重视让大家应用起来更方便,尤其接近面向对象的顺序设计理念.

线程之间的通讯

在贰个进度中,多个线程之间是有分明关联,相互通讯的

  • 貌似在子线程进行理并答复杂操作,在主线程刷新UI,这正是线程通讯

图片 1Snip20160321_5.png

常用方法

- performSelectorOnMainThread:aSelector withObject:arg waitUntilDone:wait;- performSelector:aSelector onThread:(NSThread *)thr withObject:arg waitUntilDone:wait;// 最后这个wait是指下面代码是否等这个执行完了之后再进行
  • 苹果还提供了第二种线程同步的方法NSMachPortNSPort有3个子类:NSSocketPort、NSMessagePort、NSMachPort,但在iOS下独有NSMachPort可用。使用的办法为选择线程中注册NSMachPort,在此外的线程中动用此port发送音信,则被注册线程会收到相应信息,然后最终在主线程里调用有些回调函数。能够见到,使用NSMachPort的结果为调用了任何线程的1个函数,而那多亏performSelector所做的事体,所以,NSMachPort是个鸡肋。线程间通讯应该都经过performSelector来搞定。

Grand Central Dispatch纯C语言,对NSThread进行了打包

切记:

实际不是在时下穿行队列里使用sync函数,因为会卡住线程,调用sync函数,系统会立时去实行这么些函数,但是当前线程也是串行的,他会暗中同意奉行完当前线程再去实践别的,那就产生了冲突,会卡死

GCD优势:

  • 为多核的现身运算提议的解决办法
  • 电动利用越多的CPU内核
  • 自行管理线程的生命周期(创设线程,调解职务,销毁线程等)
  • 只须要报告GCD推行什么样,GCD会活动保管职务

GCD三个核心概念

  • 职务:试行如何操作
  • 队列:用来寄存任务

GCD的行使就2个步骤

  • 定制职分
  • 规定想做的事务

将职责增加到行列中

  • GCD会自动将队列中的任务抽取,放到对应的线程中实行
  • 任务的抽出固守队列的FIFO原则:先进先出,后进后出

GCD的OC对象的内部存款和储蓄器管理GCD帮忙Cocoa内部存款和储蓄器管理机制,由此能够在付出到queue的block中随性所欲地选用Objective-C对象。每种dispatch queue维护本人的autorelease pool确定保证释放autorelease对象,不过queue不保障那一个指标实际释放的时间。如若选用消耗多量内存,况且创办一大波autorelease对象,你须求成立自个儿的autorelease pool,用来马上地放出不再采取的靶子。

一齐和异步的区分

  • 一路:只好在这里时此刻线程中进行职务,不具备翻开线程的力量
dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
  • 异步:能够在新的线程中履行职责,具备翻开线程的本事
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);

图片 2Snip20160321_7.png

例子

- touchesBegan:touches withEvent:(UIEvent *)event { [self syncConcurrent];}/** * 同步函数   主队列: */- syncMain{ NSLog(@"syncMain ----- begin"); // 1.获得主队列 dispatch_queue_t queue = dispatch_get_main_queue(); // 2.将任务加入队列 dispatch_sync(queue, ^{ NSLog(@"1-----%@", [NSThread currentThread]); }); dispatch_sync(queue, ^{ NSLog(@"2-----%@", [NSThread currentThread]); }); dispatch_sync(queue, ^{ NSLog(@"3-----%@", [NSThread currentThread]); }); NSLog(@"syncMain ----- end");}/** * 异步函数   主队列:只在主线程中执行任务 */- asyncMain{ // 1.获得主队列 dispatch_queue_t queue = dispatch_get_main_queue(); // 2.将任务加入队列 dispatch_async(queue, ^{ NSLog(@"1-----%@", [NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"2-----%@", [NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"3-----%@", [NSThread currentThread]); });}/** * 同步函数   串行队列:不会开启新的线程,在当前线程执行任务。任务是串行的,执行完一个任务,再执行下一个任务 */// 俩参数,第一个是名字,第二个是执行的优先级/*** 全局并发队列的优先级* #define DISPATCH_QUEUE_PRIORITY_HIGH 2 // 高* #define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 // 默认* #define DISPATCH_QUEUE_PRIORITY_LOW  // 低* #define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN // 后台*/- syncSerial{ // 1.创建串行队列 dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", DISPATCH_QUEUE_SERIAL); // 2.将任务加入队列 dispatch_sync(queue, ^{ NSLog(@"1-----%@", [NSThread currentThread]); }); dispatch_sync(queue, ^{ NSLog(@"2-----%@", [NSThread currentThread]); }); dispatch_sync(queue, ^{ NSLog(@"3-----%@", [NSThread currentThread]); });}/** * 异步函数   串行队列:会开启新的线程,但是任务是串行的,执行完一个任务,再执行下一个任务 */- asyncSerial{ // 1.创建串行队列 // 两个参数,第一个是名字,第二个是穿行队列 // DISPATCH_QUEUE_CONCURRENT 并行队列 // DISPATCH_QUEUE_SERIAL 串行队列 dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", DISPATCH_QUEUE_SERIAL);// 默认是穿行队列,所以第二个参数传NULL也行![Uploading Snip20160321_7_826971.png . . .]// dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", NULL); // 2.将任务加入队列 dispatch_async(queue, ^{ NSLog(@"1-----%@", [NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"2-----%@", [NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"3-----%@", [NSThread currentThread]); });}/** * 同步函数   并发队列:不会开启新的线程 * 因为还是同步函数,所以你的并发执行并没有什么卵用 */- syncConcurrent{ // 1.获得全局的并发队列 // 在你程序启动的时候,实际上GCD已经给你创建了几个全局的队列,你拿来用就好了,所以其实可以不用自己创建 // 俩参数,第一个为线程的优先级,第二个为预留参数,传0就行了 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 2.将任务加入队列 dispatch_sync(queue, ^{ NSLog(@"1-----%@", [NSThread currentThread]); }); dispatch_sync(queue, ^{ NSLog(@"2-----%@", [NSThread currentThread]); }); dispatch_sync(queue, ^{ NSLog(@"3-----%@", [NSThread currentThread]); }); NSLog(@"syncConcurrent--------end");}/** * 异步函数   并发队列:可以同时开启多条线程 */- asyncConcurrent{ // 1.创建一个并发队列 // label : 相当于队列的名字 // 俩参数,第一个是线程的名字,第二个是优先级// dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", DISPATCH_QUEUE_CONCURRENT); // 1.获得全局的并发队列 // 在你程序启动的时候,实际上GCD已经给你创建了几个全局的队列,你拿来用就好了,所以其实可以不用自己创建 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 2.将任务加入队列 dispatch_async(queue, ^{ for (NSInteger i = 0; i<10; i  ) { NSLog(@"1-----%@", [NSThread currentThread]); } }); dispatch_async(queue, ^{ for (NSInteger i = 0; i<10; i  ) { NSLog(@"2-----%@", [NSThread currentThread]); } }); dispatch_async(queue, ^{ for (NSInteger i = 0; i<10; i  ) { NSLog(@"3-----%@", [NSThread currentThread]); } }); NSLog(@"asyncConcurrent--------end");// dispatch_release;}

GCD里面线程之间的通讯

- touchesBegan:touches withEvent:(UIEvent *)event { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 图片的网络路径 NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"]; // 加载图片 NSData *data = [NSData dataWithContentsOfURL:url]; // 生成图片 UIImage *image = [UIImage imageWithData:data]; // 回到主线程  dispatch_async(dispatch_get_main_queue(), ^{ self.imageView.image = image; }); });}

在多职分编制程序中运用NSOperation时, 除了其子类NSInvocationOperation和NSBlockOperation之外, 还足以自定义子类世袭NSOperation.要做的职分很简单, 重载上面这一个函数

明天只是大约的牵线下怎样自定义NSOperation.大概理念如下:

图片 3

Snip20161122_1.png

1.一旦是自定义,前提是您自定义的类,一定也是持续自NSOperation.

@interface MyOperation : NSOperation

2.在NSOperation中有一个类,若无落到实处它,暗中同意他是怎么样也不做. 你能够重写次艺术,达成您谐和须要做的操作.官方文书档案解释如下:
The default implementation of this method does nothing. You should override this method to perform the desired task. In your implementation, do not invoke super. This method will automatically execute within an autorelease pool provided by NSOperation, so you do not need to create your own autorelease pool block in your implementation.
If you are implementing a concurrent operation, you are not required to override this method but may do so if you plan to call it from your custom start method.
这段随笔本身看的也是生搬硬套,如有分歧意见,请下方留言!

- (void) main {

NSURL *url = [NSURL URLWithString:self.urlStr];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *img = [UIImage imageWithData:data];

[[NSOperationQueue mainQueue] addOperationWithBlock:^{

    self.finishedBlock(img);
}];  
}

3.谈起底一步,也正是实例化对象.你供给懒加载一个队列.将操作增多进队列.

   MyOperation *op = [[MyOperation alloc]init];
op.urlstr = urlstr;
//实现
op.finishedBlock = ^(UIImage *img){
    self.MyImageView.image = img;
};
//把操作对象添加到队列中 最终会调用main方法
[self.queue addOperation:op];

如上纯属复习以前所学内容,收拾的一对一简单.仅供参照他事他说加以考察

  • 进程

  • 经过是指在系统中正在运作的三个应用程序

  • 每一种进度之间是独立的,每一种进度均运转在某专项使用且受保险的内部存款和储蓄器空间内

  • 线程

  • 1个进度要想举办任务,必得得有线程存在(每二个历程至罕见三个线程卡塔尔国

  • 1个进度的全数职分都在线程里做到

生手学习打理, 二弟不才, 如有错误请内定! 一同学习.

调节线程的情景
  • 开发银行线程
// 进入就绪状态->运行状态-- 执行完毕 -- > 死亡状态- start;
  • 拥塞线程
  • sleepUntilDate:date;
// 例子// [NSThread sleepUntilDate:[NSDate distantFuture]]; // [NSDate distantFuture]从现在开始到永远 [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:2]];
  • sleepForTimeInterval:(NSTimeInterval)ti;
 [NSThread sleepForTimeInterval:2]; // 让线程睡眠2秒
  • 强逼玉陨香消意况
// 强制停止线程  exit;

假如线程呜呼哀哉,就务须再次展开新的线程,不能重新调用

我们要做的事体就是将职务封装到重载的main函数中.

接下去本人用最最简便的主意演示从互连网上下载两张图片的demo.

图片 4SYOperation.png

个中定义了多少个说道OperationDelegate, 因为那些类生成的对象要与ViewController这一个目的开展通讯传值.

接下去要在重载的main函数中投入要实践的职分, 这里笔者用GCD封装的任务.

#import "SYOperation.h"static NSString * const image1 = @"http://cimage.tianjimedia.com/uploadImages/2016/09/20160916181734689.jpg";static NSString * const image2 = @"http://upload-images.jianshu.io/upload_images/2353624-611338b00993c7ba.png?imageMogr2/auto-orient/strip|imageView2/2/w/1240";@implementation SYOperation/** * 自定义的operation要把任务放到main函数中 */- main { NSLog(@"进入main函数---%@",[NSThread currentThread]); //这里我创建了一个队列组, 用来在执行两个图片文件加载结束之后打印出加载完成 dispatch_group_t groupOperaiton = dispatch_group_create(); dispatch_group_async(groupOperaiton,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSLog(@"image1线程---%@",[NSThread currentThread]); NSURL *imageURL1 = [[NSURL alloc] initWithString:image1]; NSData *imageData1 = [[NSData alloc] initWithContentsOfURL:imageURL1]; NSInteger tag1 = 1; if (imageData1) { NSLog(@"I got imageData1!!!"); dispatch_async(dispatch_get_main_queue(), ^{ if ([self.delegate respondsToSelector:@selector(imageData:withTag:)]) { [self.delegate imageData:imageData1 withTag:tag1]; } }); } }); dispatch_group_async(groupOperaiton,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSLog(@"image2线程---%@",[NSThread currentThread]); NSURL *imageURL2 = [[NSURL alloc] initWithString:image2]; NSData *imageData2 = [[NSData alloc] initWithContentsOfURL:imageURL2]; NSInteger tag2 = 2; if (imageData2) { NSLog(@"I got imageData2!!!"); dispatch_async(dispatch_get_main_queue(), ^{ if ([self.delegate respondsToSelector:@selector(imageData:withTag:)]) { [self.delegate imageData:imageData2 withTag:tag2]; } }); } }); dispatch_group_notify(groupOperaiton, dispatch_get_main_queue(), ^{ NSLog(@"加载图片任务完成!"); });}@end

在ViewController中实现加载图片开关的动作事件函数:

-  tapAction:(UIButton *)sender { //如果两个imageView的image已经有值, 单击按钮直接return. if (self.imageView1.image && self.imageView2.image) { return; } SYOperation *myOperation = [[SYOperation alloc] init]; myOperation.delegate = self; NSOperationQueue *queue = [[NSOperationQueue alloc] init]; NSLog(@"当前线程 -- %@",[NSThread currentThread]); //放到队列里就会自动开启一条新线程去执行任务. [queue addOperation:myOperation];}

落实代理方法:

- imageData:data withTag:(NSInteger)tag{ switch  { case 1: [self.imageView1 setImage:[[UIImage alloc] initWithData:data]]; NSLog(@"iamgeView1 load finish"); break; case 2: [self.imageView2 setImage:[[UIImage alloc] initWithData:data]]; NSLog(@"iamgeView2 load finish"); break; default: break; }}

调控台出口结果:

图片 5输出结果.png

能够看来那一个任务都以在区别的线程中实践的, 那样会加速实施进度, 不过要铭记并非开更加多的线程就越好, CPU急速调解时会消耗大批量的能源.

模拟器结果:

图片 6模拟器结果.gif

GCD的别样应用
  • barrier障碍

把多少个线程分开,先进行障碍此前的代码,然后实行障碍,最终推行障碍以后的代码

 dispatch_barrier_async(<#dispatch_queue_t queue#>, <#^block#>)

例子

- barrier{// dispatch_queue_t queue = dispatch_queue_create("12312312", DISPATCH_QUEUE_CONCURRENT); dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_async(queue, ^{ NSLog(@"----1-----%@", [NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"----2-----%@", [NSThread currentThread]); }); // 设置障碍 dispatch_barrier_async(queue, ^{ NSLog(@"----barrier-----%@", [NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"----3-----%@", [NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"----4-----%@", [NSThread currentThread]); });}
  • 延迟试行

dispatch_time延迟实践的代码在block里面,第叁个参数为发端时间,第二个参数为延迟的秒数,单位为微米,转变单位NSEC_PER_SEC ,第多个参数为进行的线程

 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (<#delayInSeconds#> * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ <#code to be executed after a specified delay#> });

例子

- delay{ NSLog(@"touchesBegan-----"); [self performSelector:@selector withObject:nil afterDelay:2.0]; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"run-----"); }); [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector userInfo:nil repeats:NO];}- run{ NSLog(@"run-----");}
  • 贰回性代码
// 一次执行代码,这个代码在这一整个进程里就执行一次,不会执行第二次- once{ static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSLog(@"------run"); });}
  • 敏捷迭代
// 用GCD- apply{ dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); NSString *from = @"/Users/xiaomage/Desktop/From"; NSString *to = @"/Users/xiaomage/Desktop/To"; NSFileManager *mgr = [NSFileManager defaultManager]; NSArray *subpaths = [mgr subpathsAtPath:from]; // 快速迭代 block里面有个参数为size_t index dispatch_apply(subpaths.count, queue, ^(size_t index) { NSString *subpath = subpaths[index]; NSString *fromFullpath = [from stringByAppendingPathComponent:subpath]; NSString *toFullpath = [to stringByAppendingPathComponent:subpath]; // 剪切 [mgr moveItemAtPath:fromFullpath toPath:toFullpath error:nil]; NSLog(@"%@---%@", [NSThread currentThread], subpath); });}/** * 对比:传统文件剪切 */- moveFile{ NSString *from = @"/Users/xiaomage/Desktop/From"; NSString *to = @"/Users/xiaomage/Desktop/To"; NSFileManager *mgr = [NSFileManager defaultManager]; NSArray *subpaths = [mgr subpathsAtPath:from]; for (NSString *subpath in subpaths) { NSString *fromFullpath = [from stringByAppendingPathComponent:subpath]; NSString *toFullpath = [to stringByAppendingPathComponent:subpath];// 复杂耗时操作放在子线程 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 剪切 [mgr moveItemAtPath:fromFullpath toPath:toFullpath error:nil]; }); }}
  • 设置重视分组关系
  • 创设队列
  • 创建group组对象dispatch_group_create()
  • 讲操作加到组里
  • 在组里完结有着操作后,通过 dispatch_notify(<#object#>, <#queue#>, <#notification_block#>) 方法履行下一步操作,有多个参数,第二个是哪一组的,第叁个是在哪些队列,第多少个是要实践的方法block
- group{ dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 创建一个队列组 dispatch_group_t group = dispatch_group_create(); // 1.下载图片1 dispatch_group_async(group, queue, ^{ // 图片的网络路径 NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"]; // 加载图片 NSData *data = [NSData dataWithContentsOfURL:url]; // 生成图片 self.image1 = [UIImage imageWithData:data]; }); // 2.下载图片2 dispatch_group_async(group, queue, ^{ // 图片的网络路径 NSURL *url = [NSURL URLWithString:@"http://pic38.nipic.com/20140228/5571398_215900721128_2.jpg"]; // 加载图片 NSData *data = [NSData dataWithContentsOfURL:url]; // 生成图片 self.image2 = [UIImage imageWithData:data]; }); // 3.将图片1、图片2合成一张新的图片 dispatch_group_notify(group, queue, ^{ // 开启新的图形上下文 UIGraphicsBeginImageContext(CGSizeMake); // 绘制图片 [self.image1 drawInRect:CGRectMake(0, 0, 50, 100)]; [self.image2 drawInRect:CGRectMake(50, 0, 50, 100)]; // 取得上下文中的图片 UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); // 结束上下文 UIGraphicsEndImageContext(); // 回到主线程显示图片 dispatch_async(dispatch_get_main_queue(), ^{ // 4.将新图片显示出来 self.imageView.image = image; }); });}

NSOperation的职能:合作使用NSOperation和NSOperationQueue也能兑现四十多线程编程

NSOperation和NSOperationQueue完结四线程的具体步骤

  1. 先将急需执行的操作封装到二个NSOperation对象中
  2. 然后将NSOperation对象增添到NSOperationQueue中
  3. 系统会活动将NSOperationQueue中的NSOperation抽取来
  4. 将抽出的NSOperation封装的操作放到一条新线程中进行

NSOperation是个抽象类,并不有所封装操作的力量,必需接受它的子类

应用NSOperation子类的主意有3种

  • NSInvocationOperation
  • NSBlockOperation
  • 自定义子类世襲NSOperation,完结内部相应的措施

法定文书档案:

线程锁

在多少个线程同一时间做客一个对象的时候,同一时候对该指标开展了操作,就能冒出安全隐患,用NSThread的主意须要团结管理线程锁,那就是排斥锁

  • 互斥锁的应用格式@synchronized { // 须求锁定的代码 }// 一份代码独有一把锁,多把锁是船到江心补漏迟的,依然乱的// 那些锁平日是全局变量,表示独一的

互斥锁的优劣点优点:能管用幸免因三十二线程抢夺财富变成的数码安全难点症结:须求消耗大批量的CPU能源

加互斥锁约等于要打到"线程同步"的难点线程同步的意思是:多条线程在相像条线上进行互斥锁,便是利用了线程同步能力意思正是说所有线程上一致对象是同一的!!!!

本文由澳门威利斯人发布于办公软件,转载请注明出处:实现自定义NSOperation,多线程之初识NSOperation

关键词: 澳门威利斯人 日记本 自定义 NSOperation 归纳