2019独角兽企业重金招聘Python工程师标准>>>
iOS中的线程
iOS主线程(UI线程),我们的大部分业务逻辑代码运行于主线程中。 没有特殊需求,不应引入线程增加程序复杂度。 应用场景:逻辑执行时间过长,严重影响交互体验(界面卡死)等。
iOS多线程 有三种主要方法(1)NSThread(2)NSOperation(3)GCD
下面简单介绍这三个方法
1.NSThread
调用方法如下:
如函数需要输入参数,可从object传进去。
( 1 ) [ N S T h r e a d d e t a c h N e w T h r e a d S e l e c t o r : @ s e l e c t o r ( t h r e a d In M a i n M e t h o d : ) t o T a r g e t : s e l f w i t h Object:nil];
(2) NSThread* myThread = [[NSThread alloc] initWithTarget:self selector:@selector(threadInM ainMethod:) object:nil];
[myThread start];
(3) [obj performSelectorInBackground:@selector(threadMe) withObject:nil];
提个问题:如果某个ViewController里运行了一个Thread,Thread还没结束的时候,这个ViewC ontroller被Release了,结果会如何?
经过的的测试,Thread不结束,ViewController一直保留,不会执行dealloc方法。
Demo源代码:
2.NSOperation
Demo源代码:
|
本文目录
回到顶部
前言 NSOperation有三种状态
NSOperationQueue只有当它管理的所有操作的isFinished属性全标为YES以后操作才停止出列,也就是队列停止运行,所以正确实现这个方法对于避免死锁很关键。
1.上一讲简单介绍了NSThread的使用,虽然也可以实现多线程编程,但是需要我们去管理线程的生命周期,还要考虑线程同步、加锁问题,造成一些性能上的开销。我们也可以配合使用NSOperation和NSOperationQueue实现多线程编程,实现步骤大致是这样的: 1> 先将需要执行的操作封装到一个NSOperation对象中 2> 然后将NSOperation对象添加到NSOperationQueue中 3> 系统会自动将NSOperation中封装的操作放到一条新线程中执行 在此过程中,我们根本不用考虑线程的生命周期、同步、加锁等问题 下面列举一个应用场景,比如微博的粉丝列表: 每一行的头像肯定要从新浪服务器下载图片后才能显示的,而且是需要异步下载。这时候你就可以把每一行的图片下载操作封装到一个NSOperation对象中,上面有6行,所以要创建6个NSOperation对象,然后添加到NSOperationQueue中,分别下载不同的图片,下载完毕后,回到对应的行将图片显示出来。
2.默认情况下,NSOperation并不具备封装操作的能力,必须使用它的子类,使用NSOperation子类的方式有3种: 1> NSInvocationOperation 2> NSBlockOperation 3> 自定义子类继承NSOperation,实现内部相应的方法 这讲先介绍如何用NSOperation封装一个操作,后面再结合NSOperationQueue来使用。
回到顶部 一、NSInvocationOperation1 NSInvocationOperation *operation = [[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run:) object:@"mj"] autorelease]; 2 [operation start]; * 第1行初始化了一个NSInvocationOperation对象,它是基于一个对象和selector来创建操作 * 第2行调用了start方法,紧接着会马上执行封装好的操作,也就是会调用self的run:方法,并且将@"mj"作为方法参数 * 这里要注意:默认情况下,调用了start方法后并不会开一条新线程去执行操作,而是在当前线程同步执行操作。只有将operation放到一个NSOperationQueue中,才会异步执行操作。
回到顶部 二、NSBlockOperation1.同步执行一个操作1 NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^(){
2 NSLog(@"执行了一个新的操作"); 3 }]; 4 // 开始执行任务 5 [operation start]; * 第1行初始化了一个NSBlockOperation对象,它是用一个Block来封装需要执行的操作 * 第2行调用了start方法,紧接着会马上执行Block中的内容 * 这里还是在当前线程同步执行操作,并没有异步执行
2.并发执行多个操作
1 NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^(){
2 NSLog(@"执行第1次操作,线程:%@", [NSThread currentThread]); 3 }]; 4 5 [operation addExecutionBlock:^() { 6 NSLog(@"又执行了1个新的操作,线程:%@", [NSThread currentThread]); 7 }]; 8 9 [operation addExecutionBlock:^() { 10 NSLog(@"又执行了1个新的操作,线程:%@", [NSThread currentThread]); 11 }]; 12 13 [operation addExecutionBlock:^() { 14 NSLog(@"又执行了1个新的操作,线程:%@", [NSThread currentThread]); 15 }]; 16 17 // 开始执行任务 18 [operation start];
* 第1行初始化了一个NSBlockOperation对象 * 分别在第5、9、13行通过addExecutionBlock:方法添加了新的操作,包括第1行的操作,一共封装了4个操作 * 在第18行调用start方法后,就会并发地执行这4个操作,也就是会在不同线程中执行 1 2013-02-02 21:38:46.102 thread[4602:c07] 又执行了1个新的操作,线程: 可以看出,每个操作所在线程的num值都不一样,说明是不同线程
回到顶部 三、NSOperation的其他用法1.取消操作operation开始执行之后, 默认会一直执行操作直到完成,我们也可以调用cancel方法中途取消操作 [operation cancel];
2.在操作完成后做一些事情如果想在一个NSOperation执行完毕后做一些事情,就调用NSOperation的setCompletionBlock方法来设置想做的事情 operation.completionBlock = ^() {
NSLog(@"执行完毕"); }; 当operation封装的操作执行完毕后,就会回调Block里面的内容
NSOperation进阶 优先级跟NSThread一样,NSOpertion也可以设置优先级。 执行顺序(依赖)有些时候想要控制执行顺序,使用NSOpreation会方便多了,使用NSOpreation的Dependency就可以实现这种功能。 上面先执行第一个operation1,等operation1返回isFinish为YES,即operation1完成了,才会执行operation2。 注意死锁:一定不可以循环依赖,像A依赖B,B依赖A,一定不要这样做 CompletionBlock这个比较容易理解,就是每个NSOperation执行完毕之后,就会执行该block 执行结果 取消如前面所说,NSOperation有三种状态,isReady -> isExecuting -> isFinish, 如果在Ready的状态中对NSOperation进行取消,NSOperation会进入Finish状态。但是Operation已经开始执行了,就会一直运行到结束,或者由我们进行显示取消。也就是说Operation已经在executing状态,我们调用cancle方法系统不会中止线程的,这需要我们在任务过程中检测取消事件,并中止线程的执行,还要注意一点我们要释放内存或资源。还是看一下实例代码:
这种取消跟NSThread有点相似,调用cancle不会退出线程,需要你自已去中止线程,再exit; 回到顶部 四、自定义NSOperation如果NSInvocationOperation和NSBlockOperation不能满足需求,我们可以直接新建子类继承NSOperation,并添加任何需要执行的操作。如果只是简单地自定义NSOperation,只需要重载-(void)main这个方法,在这个方法里面添加需要执行的操作。
下面写个子类DownloadOperation来下载图片
不支持并发 1.继承NSOperation,重写main方法DownloadOperation.h
#import
DownloadOperation.m
1 #import "DownloadOperation.h" 2 3 @implementation DownloadOperation 4 @synthesize delegate = _delegate; 5 @synthesize imageUrl = _imageUrl; 6 7 // 初始化 8 - (id)initWithUrl:(NSString *)url delegate:(id
* 在第22行重载了main方法,等会就把下载图片的代码写到这个方法中 * 如果这个DownloadOperation是在异步线程中执行操作,也就是说main方法在异步线程调用,那么将无法访问主线程的自动释放池,所以在第24行创建了一个属于当前线程的自动释放池
2.正确响应取消事件* 默认情况下,一个NSOperation开始执行之后,会一直执行任务到结束,就比如上面的DownloadOperation,默认会执行完main方法中的所有代码。 * NSOperation提供了一个cancel方法,可以取消当前的操作。 * 如果是自定义NSOperation的话,需要手动处理这个取消事件。比如,一旦调用了cancel方法,应该马上终止main方法的执行,并及时回收一些资源。 * 处理取消事件的具体做法是:在main方法中定期地调用isCancelled方法检测操作是否已经被取消,也就是说是否调用了cancel方法,如果返回YES,表示已取消,则立即让main方法返回。 * 以下地方可能需要调用isCancelled方法:
1 - (void)main { 2 // 新建一个自动释放池,如果是异步执行操作,那么将无法访问到主线程的自动释放池 3 @autoreleasepool { 4 if (self.isCancelled) return; 5 6 // 获取图片数据 7 NSURL *url = [NSURL URLWithString:self.imageUrl]; 8 NSData *imageData = [NSData dataWithContentsOfURL:url]; 9 10 if (self.isCancelled) { 11 url = nil; 12 imageData = nil; 13 return; 14 } 15 16 // 初始化图片 17 UIImage *image = [UIImage imageWithData:imageData]; 18 19 if (self.isCancelled) { 20 image = nil; 21 return; 22 } 23 24 if ([self.delegate respondsToSelector:@selector(downloadFinishWithImage:)]) { 25 // 把图片数据传回到主线程 26 [(NSObject *)self.delegate performSelectorOnMainThread:@selector(downloadFinishWithImage:) withObject:image waitUntilDone:NO]; 27 } 28 } 29 }
* 在第4行main方法的开头就先判断operation有没有被取消。如果被取消了,那就没有必要往下执行了 * 经过第8行下载图片后,在第10行也需要判断操作有没有被取消 * 总之,执行了一段比较耗时的操作之后,都需要判断操作有没有被取消 * 图片下载完毕后,在第26行将图片数据传递给了代理(delegate)对象
支持并发 自定义并发的NSOperation就麻烦多了,需要实现以下方法,我们可以看一下下面这个表(来自苹果官方):
3.GCD 见单独一篇文章 |
|