本篇主要说一下多线程的东西,因为东西比较多,所以直接拿出一整个篇幅来说它了。
3、 多线程的底层实现?
1、先说一下什么是进程,什么是线程、什么是多线程。
进程是资源分配的单位,线程是调度运行的基本单位。多线程简而言之就是一个应用程序中,多个线程同步执行;多用于处理用户界面响应和网络访问的处理的同事进行,通常用子线程处理如网络访问等一些比较耗时的操作,同时不影响主线程在用户界面对用户操作的响应。
2、 Mach
Mach是第一个以多线程方式处理任务的系统,因此多线程的底层实现机制是基于Mach的线程,开发中很少用Mach级的线程,因为Mach级的线程没有提供多线程的基本特征,线程之间是独立的。
3、 开发中实现多线程的方案
1、C语言的pthread
2、OC的NSThread
3、 GCD
4、NSOperation和NSOperationQueue
4、线程间怎么通信?
这里借用这个问题,比较详细的回顾一下比较常用的三种多线程方案: NSThread、GCD、NSOperation和NSOperationQueue。
1、NSThread
先看它的线程间通讯:
1 //去子线程执行耗时任务 2 // [self performSelectorInBackground:@selector(downloadSomething:) withObject:url]; 3 4 // NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(downloadSomething:) object:url]; 5 // // 设置线程的优先级(0.0 - 1.0,1.0最高级) 6 // //thread.threadPriority = 1.0; 7 // [thread start]; 8 9 [NSThread detachNewThreadSelector:@selector(downloadSomething:) toTarget:self withObject:url];
去子线程执行任务的三种方式。
1 //回到主线程刷新界面 2 // [self performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:NO]; 3 4 // [self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:NO]; 5 6 [self.imageView performSelector:@selector(setImage:) onThread:[NSThread mainThread] withObject:image waitUntilDone:NO];
耗时任务执行完毕之后回主线程刷新界面的三种方式。
还有一些其他的简单操作可以去demo看一下,这里就不多赘述了。
NSThread比其他两种多线程方案较轻量级,更直观地控制线程对象。但是需要自己管理线程的生命周期,线程同步。线程同步对数据的加锁会有一定的系统开销。
2、GCD
GCD的线程间通讯:
1 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 2 3 NSData *data = [NSData dataWithContentsOfURL:url]; 4 NSData *data1 = [NSData dataWithContentsOfURL:url1]; 5 UIImage *image = [UIImage imageWithData:data]; 6 UIImage *image1 = [UIImage imageWithData:data1]; 7 dispatch_async(dispatch_get_main_queue(), ^{ 8 self.imageView.image = image; 9 self.imageView1.image = image1; 10 }); 11 12 });
在子线程下载两张图片,回到主线程更新界面。
下面再看一下Dispatch Group:
Dispatch Group可以用来阻塞一个线程, 直到group关联的所有的任务完成执行。适用于有时候你必须等待任务完成的结果,然后才能继续后面的处理的情况。但是下面的这种场景其实个人认为是不需要这么做的,用上面的方式就好了。这里只为演示代码。
1 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 2 3 dispatch_async(queue, ^{ 4 dispatch_group_t group = dispatch_group_create(); 5 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 6 __block UIImage *image = nil; //注意block给image赋值,必须加__block前缀 7 __block UIImage *image1 = nil; 8 //往组中添加任务 9 dispatch_group_async(group, queue, ^{ 10 NSData *data = [NSData dataWithContentsOfURL:url]; 11 image = [UIImage imageWithData:data]; 12 }); 13 //往组中添加任务 14 dispatch_group_async(group, queue, ^{ 15 NSData *data1 = [NSData dataWithContentsOfURL:url1]; 16 image1 = [UIImage imageWithData:data1]; 17 }); 18 // 等待组中的任务执行完毕,回到主线程执行block回调 19 dispatch_group_notify(group, dispatch_get_main_queue(), ^{ 20 self.imageView.image = image; 21 self.imageView1.image = image1; 22 }); 23 });
还有以下GCD的其他用法,写在demo里了,这里就不再赘述。下面看NSOperation和NSOperationQueue。
3、NSOperation和NSOperationQueue
1 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; 2 3 __block UIImage *image0 = nil; 4 __block UIImage *image1 = nil; 5 6 // 创建3个操作 7 NSOperation *a = [NSBlockOperation blockOperationWithBlock:^{ 8 NSData *data = [NSData dataWithContentsOfURL:url0]; 9 image0 = [UIImage imageWithData:data]; 10 NSLog(@"当前调用线程:%@", [NSThread currentThread]); 11 }]; 12 NSOperation *b = [NSBlockOperation blockOperationWithBlock:^{ 13 NSData *data = [NSData dataWithContentsOfURL:url1]; 14 image1 = [UIImage imageWithData:data]; 15 NSLog(@"当前调用线程:%@", [NSThread currentThread]); 16 }]; 17 NSOperation *c = [NSBlockOperation blockOperationWithBlock:^{ 18 [self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image0 waitUntilDone:NO]; 19 [self.imageView1 performSelectorOnMainThread:@selector(setImage:) withObject:image1 waitUntilDone:NO]; 20 }]; 21 22 // 添加依赖 23 [b addDependency:a]; 24 // [c addDependency:a]; 25 [c addDependency:b]; 26 27 // 执行操作 28 [queue addOperation:a]; 29 [queue addOperation:b]; 30 [queue addOperation:c];
GCD和NSOperationQueue都属于不需要自己直接操作线程的,本人一般情况下都直接用GCD比较多,一来因为它高效,二来简洁方便。但是在像上面需要顺序执行,也就是需要用到依赖的时候,用NSOperationQueue比较方便。
同样的,其他关于NSOperation和NSOperationQueue的使用在这里不多赘述,希望再多了解一些的可以自行去demo看一下代码。
demo:https://github.com/alan12138/Interview-question/tree/master/4
5、GCD内部怎么实现的?
1、 iOS和OS X的核心是XNU内核,GCD是基于XNU内核实现的
2、 GCD的API全部在libdispatch库中
3、 GCD的底层实现主要有Dispatch Queue和Dispatch Source
Dispatch Queue :管理block(操作)
Dispatch Source :处理事件
6、 你用过NSOperationQueue么?如果用过或者了解的话,你为什么要使用NSOperationQueue,实现了什么?请描述它和GCD的区别和类似的地方(提示:可以从两者的实现机制和适用范围来描述)。
1、GCD是纯C语言的API,NSOperationQueue是基于GCD的OC版本封装
2、 GCD的执行速度比NSOperationQueue快
3、GCD只支持FIFO的队列,NSOperationQueue可以很方便地调整执行顺序、设置最大并发数量
4、 NSOperationQueue可以在轻松在Operation间设置依赖关系,而GCD需要写很多的代码才能实现
5、 NSOperationQueue支持KVO,可以监测operation是否正在执行(isExecuted)、是否结束(isFinished),是否取消(isCanceld)
6、如何选择:任务之间不太互相依赖:GCD;任务之间有依赖\或者要监听任务的执行情况:NSOperationQueue
7.、既然提到GCD,那么问一下在使用GCD以及block时要注意些什么?它们两是一回事儿么?block在ARC中和传统的MRC中的行为和用法有没有什么区别,需要注意些什么?
Block的使用注意:
1、 block的内存管理(前面的题有讲到block)
2、防止强引用循环
避免强引用循环:
* 非ARC(MRC):__block(还有修改外部临时变量)
* ARC:__weak\__unsafe_unretained