今天我们讲解几道这两天遇到的面试题--GCD编程的.题目很不错,很考究关于GCD的基本概念和使用.

对于基本的概念,本人博客已在前面讲过,本篇主要以面试题来讲解.大家可看一下本人关于GCD的基本讲解 https://www.cnblogs.com/guohai-stronger/p/9038567.html

GCD编程的核心就是dispatch队列, dispatch block的执行都会最终放到某个队列中执行.我们直接以题目来讲解:

【例1】GCD 1

- (void)viewDidLoad {
[super viewDidLoad];
[self testGCD];
} - (void)testGCD {
//并行队列
dispatch_queue_t queue = dispatch_queue_create("zxy", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"");
dispatch_async(queue, ^{
NSLog(@"");
dispatch_async(queue, ^{
NSLog(@"");
});
NSLog(@"");
});
NSLog(@"");
}

讲解

  1. 首先看创建队列是并行队列,无需等待;
  2. 首先打印出 1 ,然后看dispatch_async,将dispatch_async代码段整个看做事物,对于并行队列,并不会等待,然后绕过dispatch_async事物,执行下面的内容,执行完下面内容后再次执行该事物;
  3. 紧接着打印出 5 ,执行完打印5,再次会执行dispatch_async整个代码段,看里面的内容有打印出 2 ,还有一个dispatch_async,还有打印出 4
       NSLog(@"2");
    dispatch_async(queue, ^{
    NSLog(@"3");
    });
    NSLog(@"4");
  4. 又因为是dispatch_async,以及后面打印出 4 ,又是并行队列,异步执行,所以会先执行打印
  5. 执行完打印出 4 后,就会执行

验证结果截图

【例2】GCD 2

- (void) testGCD2 {
dispatch_queue_t queue = dispatch_queue_create("zxy", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"1");
dispatch_async(queue, ^{
NSLog(@"2");
dispatch_sync(queue, ^{
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5");
}

结果如下:

【例3】GCD3

- (void) testGCD2 {
dispatch_queue_t queue = dispatch_queue_create("zxy", DISPATCH_QUEUE_SERIAL);
NSLog(@"1");
dispatch_async(queue, ^{
NSLog(@"2");
dispatch_async(queue, ^{
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5");
}

结果如下:

【例4】GCD  4

- (void)viewDidLoad {
[super viewDidLoad];
[self testGCD];
} - (void)testGCD {
//串行队列
dispatch_queue_t queue = dispatch_queue_create("zxy", DISPATCH_QUEUE_SERIAL);
NSLog(@"");
dispatch_async(queue, ^{
NSLog(@"");
dispatch_sync(queue, ^{
NSLog(@"");
});
NSLog(@"");
});
NSLog(@"");
}

讲解:

  1. 首先是串行队列,只能按顺序执行
  2. 首先打印出  ,然后是dispatch_async会开启线程,里面的内容会慢些执行,执行下面的打印 5
  3. 打印出 ,然后回到dispatch_async里面,异步执行会开启线程,发现里面有NSLog(@“2”),dispatch_syncy是个同步执行,NSLog(@“4”),然后先打印出
  4. 因为外面dispatch_async,打印出2 之后,开始走 NSLog(@“4”),然后再回到同步里面NSLog(@“3”),然后我们发现dispatch_syncy是个同步执行,也就是不执行完同步里面的NSLog(@“3”),就不可能走NSLog(@“4”),所以说两边都在等着对方,造成了死锁。
  5. 在程序中会崩溃

验证结果截图

例5】GCD 5

- (void)viewDidLoad {
[super viewDidLoad]; //问题:以下代码是在主线程执行的,会不会产生死锁
NSLog(@"执行任务1--%@", NSThread.currentThread);
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{
NSLog(@"执行任务2--%@",NSThread.currentThread);
});
NSLog(@"执行任务3--%@",NSThread.currentThread);
}

讲解:

  1. 首先是viewDidLoad是在主队列,首先打印出 1
  2. 执行任务2也是在主队列下queue = dispatch_get_main_queue,由于队列按照FIFO顺序执行,开始将viewDidLoad加入到队列中,然后将“执行任务2”任务加入到队列中,viewDidLoad包含了执行任务2,执行打印任务2不执行完,就不可能执行完viewDidLoad,然后执行打印任务2必须等待着viewDidLoad执行完之后才能执行,导致了死锁!

例6】GCD 6

- (void)viewDidLoad {
[super viewDidLoad]; //问题:以下代码是在主线程执行的,会不会产生死锁
NSLog(@"执行任务1--%@", NSThread.currentThread);
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^{
NSLog(@"执行任务2--%@",NSThread.currentThread);
});
NSLog(@"执行任务3--%@",NSThread.currentThread);
}

讲解:

  1. 首先是viewDidLoad是在主队列,首先打印出 1
  2. 由于是dispatch_async在主队列下不会开启新的线程,所以还是在当前线程执行,其中还有一个是dispatch_async不要求立马在当前线程同步执行任务,所以先执行任务3,不会造成死锁

【例7】GCD 7

dispatch_queue_t queue = dispatch_queue_create("zxy", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"哈哈哈 1");
});
dispatch_async(queue, ^{
NSLog(@"哈哈哈 2");
});
dispatch_sync(queue, ^{
NSLog(@"哈哈哈 3");
});
////////////////////
NSLog(@"哈哈哈 0");
dispatch_async(queue, ^{
NSLog(@"哈哈哈 7");
});
dispatch_async(queue, ^{
NSLog(@"哈哈哈 8");
});
dispatch_async(queue, ^{
NSLog(@"哈哈哈 9");
});

A 1230789   B 1237890  C 3120798  D 2137890  C 3012789

上面五种答案,哪一个是对的?

讲解:

  1. 首先是并行队列 DISPATCH_QUEUE_CONCURRENT,可以并行的执行任务
  2. 在哈哈哈 3中是同步执行,所以此后面的代码在3没有执行完之前是不可能执行的,而前面的哈哈哈 1 和哈哈哈 2都是异步,是可以执行完的,但是当哈哈哈3执行完会紧接着执行 哈哈哈0,所以不可能出现7,8,9在0之前执行完
  3. 从上可以判断出 A  C  E 是可能出现的

将上面的代码运行到xcode得到如下:E答案

【例8】GCD面试题8

我们将DISPATCH_QUEUE_CONCURRENT改为DISPATCH_QUEUE_SERIAL

因为将并行队列改为串行队列,所有任务按部就班执行,所以结果如下(不加以讲解)

【例9】GCD面试题9

- (void) testGCD{
__block NSInteger a = ;
while (a < ) {
dispatch_async(dispatch_get_global_queue(, ), ^{
a++;
NSLog(@"%ld======%@", a, [NSThread currentThread]);
});
}
NSLog(@"卧槽无情%ld", a);
}

讲解:

  1. 首先不可能打印出0,因为有while循环,while不走完,是不可能走后面的.a = 0, 然后进入while循环, 因为是异步,所以就会开启线程执行a ++ , 可能开出的线程状态不好,所以a可能继续为0, ……
  2. 但是在某个时刻,可能线程64状态返回,执行a++, 所以a = 1 ,……
  3. 所以a的最后值是大于等于100(>=100)

可能状态还不错,从上面看出结果为100

【例10】GCD 10

- (void)viewDidLoad {
[super viewDidLoad];
dispatch_queue_t queue = dispatch_get_global_queue(, );
dispatch_async(queue, ^{
NSLog(@"");
[self performSelector:@selector(test) withObject:nil afterDelay:];
NSLog(@"");
});
} - (void)test{
NSLog(@"");
}

讲解:

  1. performSelector:withObject:afterDelay:的本质是往Runloop中添加定时器
  2. 子线程默认是没有开启Runloop(dispatch_async开启了子线程)

【例11】GCD 11

- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"");
[self performSelector:@selector(test) withObject:nil afterDelay:];
NSLog(@"");
} - (void)test{
NSLog(@"");
}

讲解:

  1. performSelector:withObject:afterDelay:的本质是往Runloop中添加定时器
  2. viewDidLoad默认在主线程,主线程默认是开启Runloop,所以执行结果是有打印2

为什么先执行3,因为执行2在runloop中唤醒的时候执行!

【例12】GCD 12

- (void)viewDidLoad {
[super viewDidLoad];
dispatch_queue_t queue = dispatch_get_global_queue(, );
dispatch_async(queue, ^{
NSLog(@"");
[self performSelector:@selector(test) withObject:nil];
NSLog(@"");
});
} - (void)test{
NSLog(@"");
}

讲解:

  • performSelector:withObject:的本质是msgSend,相当于【self test】,和后面加afterDelay完全不一样,Runloop中添加定时器,Runloop有休眠和唤醒状态

【例13】GCD 13

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
NSThread *thread = [[NSThread alloc]initWithBlock:^{
NSLog(@"");
}];
[thread start];
[self performSelector:@selector(test) onThread:thread withObject:nil waitUntilDone:YES];
} - (void)test{
NSLog(@"");
}

讲解:

  • performSelector放在了子线程NSThread,然而当执行完打印1后thread就销毁了,导致了调用performSelector就失败了导致崩溃,如果想要打印出2,可以将线程放在runloop中,
  • 如下
  • - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    NSThread *thread = [[NSThread alloc]initWithBlock:^{
    NSLog(@"");
    [[NSRunLoop currentRunLoop]addPort:[[NSPort alloc]init] forMode:NSDefaultRunLoopMode];
    [[NSRunLoop currentRunLoop]runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
    }];
    [thread start];
    [self performSelector:@selector(test) onThread:thread withObject:nil waitUntilDone:YES];
    } - (void)test{
    NSLog(@"");
    }

或者将任务放在了主线程中

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
NSThread *thread = [[NSThread alloc]initWithBlock:^{
NSLog(@"");
}];
[thread start];
[self performSelectorOnMainThread:@selector(test) withObject:nil waitUntilDone:YES];
} - (void)test{
NSLog(@"");
}

以上就是GCD的经典面试题,其实看了这几道面试题,发现还都是GCD的基本内容,串行队列,并行队列,以及同步异步是否开启线程的基本概念,希望通过本次讲解,大家对GCD的认识会更上一层,谢谢!!!

GCD 面试题的更多相关文章

  1. [置顶] 阿里IOS面试题之多线程选用NSOperation or GCD

    今天早上接到了阿里从杭州打过来的电话面试.虽然近期面试了一些大中型的互联网企业,但是跟素有“IT界的黄浦军校”的阿里面试官接触还是不免紧张. 面试持续了三四十分钟吧,大部分问题都是简历上的项目经验而来 ...

  2. GCD的使用和面试题集锦

    GCD 分为异步和同步 异步: dispatch_async ( 参数1 , { } 同步: dispatch_sync( 参数1 , { } 参数1 :队列 队列分为两种: dispatch_get ...

  3. 蓝桥杯-- 历届试题 核桃的数量 (gcd)

      历届试题 核桃的数量   时间限制:1.0s   内存限制:256.0MB        问题描述 小张是软件项目经理,他带领3个开发组.工期紧,今天都在加班呢.为鼓舞士气,小张打算给每个组发一袋 ...

  4. iOS面试题

    一个区分度很大的面试题 考察一个面试者基础咋样,基本上问一个 @property 就够了: @property 后面可以有哪些修饰符? 线程安全的: atomic,nonatomic 访问权限的 re ...

  5. iOS之2016面试题二

    前言 招聘高峰期来了,大家都非常积极地准备着跳槽,那么去一家公司面试就会有一堆新鲜的问题,可能不会,也可能会,但是了解不够深.本篇文章为群里的小伙伴们去要出发公司的笔试题,由笔者整理并提供笔者个人参考 ...

  6. iOS之2016面试题一

    序言 招聘高峰期来了,大家都非常积极地准备着跳槽,那么去一家公司面试就会有一堆新鲜的问题,可能不会,也可能会,但是了解不够深.本篇文章为群里的小伙伴们去宝库公司的笔试题,由笔者整理并提供笔者个人参考答 ...

  7. iOS面试题总结(一)

    面试题总结 1.#import 跟#include.@class有什么区别?#import<> 跟 #import""又什么区别? include和#import都能完 ...

  8. 2016年iOS笔试题

    收集了一些ios面试的一些基础的试题,其中也有一些较难的 1.请简述UIView与CALayer有什么不同.2.Block什么情况下会保留实体内引用到外部对象,什么时候要用__block或__weak ...

  9. 李洪强经典面试题145-Runloop

    李洪强经典面试题145-Runloop   Runloop 什么是 Runloop? 从字面上讲就是运行循环. 它内部就是do-while循环,在这个循环内部不断地处理各种任务. 一个线程对应一个Ru ...

随机推荐

  1. Java SPI、servlet3.0与@HandlesTypes源码分析

    关于Java SPI与servlet3.0的应用,这里说的很精炼,链接地址如下. https://blog.csdn.net/pingnanlee/article/details/80940993 以 ...

  2. MySQL常用sql语句-----数据库操作

    在数据库操作中,操作基本都是围绕增删改查来操作.简称CRUD C创建创建 R读取/检索查询 U Update修改 D删除删除 在数操作数据库时,所有的数据库语句都要以分号结束 数据库操作不区分大小写 ...

  3. OSI七层模型和五层TCP/IP协议

    1.查公网ip的方法: windows,打开浏览器,访问百度,搜IP即可 linux:curl ifconfig.me 2.OSI七层模型 ==网络工程师:== 物理层 1层,通信介质的信号到数字信号 ...

  4. jQuery插件图片懒加载lazyload

    来自XXX的前言: 什么是ImageLazyLoad技术 在页面上图片比较多的时候,打开一张页面必然引起与服务器大数据量的 交互.尤其是对于高清晰的图片,占的几M的空间.ImageLazyLoad技术 ...

  5. vuex状态管理详细使用方法

    1安装:vue ui或cnpm install vuex 2/使用import vuex from 'vuex' vue.use(vuex) var store = new Vuex.store({  ...

  6. 如何把当前时间戳转化为时间格式HH:MM:SS

    获取当前时间戳 var timestamp = new Date().getTime() 获取当前时间(从1970.1.1开始的毫秒数) // 创建一个函数function timestampToTi ...

  7. TensorFlow深度学习基础与应用实战高清视频教程

    TensorFlow深度学习基础与应用实战高清视频教程,适合Python C++ C#视觉应用开发者,基于TensorFlow深度学习框架,讲解TensorFlow基础.图像分类.目标检测训练与测试以 ...

  8. 开根号 HYSBZ - 3211

    区间修改+区间查询(线段树板子题) 另外因为1e9内的数开5次根号必定为1或0,所以我们可以提前打表i<=sqrt[1e9], s[i]=sqrt(i).这样每次改值不必再调用系统的sqrt: ...

  9. element 自定义 el-loading

    前言 虽说,el-loading 自带的loading效果已经满足日常的需求看 但是,美术大大觉得太low  我要我设计的,我不要你觉得我要我觉得 需求 自定义el-loading 正文 从官方需求文 ...

  10. python pytesseract使用

    正确使用方法 1.tesseract-orc安装 tesseract-ocr-setup-3.05.00dev.exe下载 2.pytesseract pip install pytesseract ...