前言
不积跬步无以至千里,不积小流无以成江海。学如逆水行舟,不进则退。我是平平无奇游荡于各平台的搬运工。每天学习半小时,健康幸福一辈子。今天给大家讲讲ui方面相关的面试题。废话不多说,直接给大家上干货,希望能对你有所帮助,优秀的人已经点赞了。
一、进程:
1.进程是一个具有一定独立功能的程序关于某次数据集合的一次运行活动,它是操作系统分配资
源的基本单元.
2.进程是指在系统中正在运行的一个应用程序,就是一段程序的执行过程,我们可以理解为手机上
的一个 app.
3.每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内,拥有独立运行所需
的全部资源
二 、线程
1.程序执行流的最小单元,线程是进程中的一个实体.
2.一个进程要想执行任务,必须至少有一条线程.应用程序启动的时候,系统会默认开启一条线程,
也就是主线程
三、进程和线程的关系
1.线程是进程的执行单元,进程的所有任务都在线程中执行
2.线程是 CPU 分配资源和调度的最小单位
3.一个程序可以对应多个进程(多进程),一个进程中可有多个线程,但至少要有一条线程
4.同一个进程内的线程共享进程资源
四、多进程
打开mac的活动监视器,可以看到很多歌进程同时运行
-
进程是程序在计算机上的一次执行活动。当你运行一个程序,你就启动了一个进程。显然,程序
是死的(静态的),进程是活的(动态的)。
-
进程可以分为系统进程和用户进程。凡是用于完成操作系统的各种功能的进程就是系统进程,它
们就是处于运行状态下的操作系统本身;所有由用户启动的进程都是用户进程。进程是操作系统进
行资源分配的单位。
-
进程又被细化为线程,也就是一个进程下有多个能独立运行的更小的单位。在同一个时间里,同
一个计算机系统中如果允许两个或两个以上的进程处于运行状态,这便是多进程。
五、多线程
1.同一时间,CPU 只能处理 1 条线程,只有 1 条线程在执行。多线程并发执行,其实是 CPU 快速地在多条 线程之间调度(切换)。如果 CPU 调度线程的时间足够快,就造成了多线程并发执行的假象
- 如果线程非常非常多,CPU 会在 N 多线程之间调度,消耗大量的 CPU 资源,每条线程被调度执行的频次
会降低(线程的执行效率降低)
- 多线程的优点:
能适当提高程序的执行效率
能适当提高资源利用率(CPU、内存利用率)
- 多线程的缺点:
开启线程需要占用一定的内存空间(默认情况下,主线程占用 1M,子线程占用 512KB),如果开启大量的
线程,会占用大量的内存空间,降低程序的性能
线程越多,CPU 在调度线程上的开销就越大
程序设计更加复杂:比如线程之间的通信、多线程的数据共享
六、任务
就是执行操作的意思,也就是在线程中执行的那段代码。在 GCD 中是放在 block 中的。执行任务有两种
方式:同步执行(sync)和异步执行(async)
同步(Sync):同步添加任务到指定的队列中,在添加的任务执行结束之前,会一直等待,直到队列里面的
任务完成之后再继续执行,即会阻塞线程。只能在当前线程中执行任务(是当前线程,不一定是主线程),
不具备开启新线程的能力。
异步(Async):线程会立即返回,无需等待就会继续执行下面的任务,不阻塞当前线程。可以在新的线程中
执行任务,具备开启新线程的能力(并不一定开启新线程)。如果不是添加到主队列上,异步会在子线程中
执行任务
七、队列
队列(Dispatch Queue):这里的队列指执行任务的等待队列,即用来存放任务的队列。队列是一种特殊 的线性表,采用FIFO(先进先出)的原则,即新任务总是被插入到队列的末尾,而读取任务的时候总是从 队列的头部开始读取。每读取一个任务,则从队列中释放一个任务在 GCD 中有两种队列:串行队列和并发队列。两者都符合 FIFO(先进先出)的原则。两者的主要区别是:执行顺序不同,以及开启线程数不同。
- 串行队列(Serial Dispatch Queue):
同一时间内,队列中只能执行一个任务,只有当前的任务执行完成之后,才能执行下一个任务。(只
开启一个线程,一个任务执行完毕后,再执行下一个任务)。主队列是主线程上的一个串行队列,是
系统自动为我们创建的
- 并发队列(Concurrent Dispatch Queue):
同时允许多个任务并发执行。(可以开启多个线程,并且同时执行任务)。并发队列的并发功能只有
在异步(dispatch_async)函数下才有效
八、iOS中的多线程
主要有三种:NSThread、NSoperationQueue、GCD
1.NSThread:轻量级别的多线程技术
是我们自己手动开辟的子线程,如果使用的是初始化方式就需要我们自己启动,如果使用的是构造器方式
它就会自动启动。只要是我们手动开辟的线程,都需要我们自己管理该线程,不只是启动,还有该线程使
用完毕后的资源回收
需要注意的是:如果是带 afterDelay 的延时函数,会在内部创建一个 NSTimer,然后添加到当前线程的
Runloop 中。也就是如果当前线程没有开启 runloop,该方法会失效。在子线程中,需要启动 runloop(注
意调用顺序)
2、GCD 对比 NSOprationQueue
我们要明确 NSOperationQueue 与 GCD 之间的关系
GCD 是面向底层的 C 语言的 API,NSOpertaionQueue 用 GCD 构建封装的,是 GCD 的高级抽象。
1、GCD 执行效率更高,而且由于队列中执行的是由 block 构成的任务,这是一个轻量级的数据结构,写起
来更方便
2、GCD 只支持 FIFO 的队列,而 NSOperationQueue 可以通过设置最大并发数,设置优先级,添加依赖关系
等调整执行顺序
3、NSOperationQueue 甚至可以跨队列设置依赖关系,但是 GCD 只能通过设置串行队列,或者在队列内添
加 barrier(dispatch_barrier_async)任务,才能控制执行顺序,较为复杂
4、NSOperationQueue 因为面向对象,所以支持 KVO,可以监测 operation 是否正在执行(isExecuted)、
是否结束(isFinished)、是否取消(isCanceld)
-
实际项目开发中,很多时候只是会用到异步操作,不会有特别复杂的线程关系管理,所以苹果推崇的
且优化完善、运行快速的 GCD 是首选
-
如果考虑异步操作之间的事务性,顺序行,依赖关系,比如多线程并发下载,GCD 需要自己写更多的
代码来实现,而 NSOperationQueue 已经内建了这些支持
-
不论是 GCD 还是 NSOperationQueue,我们接触的都是任务和队列,都没有直接接触到线程,事实上
线程管理也的确不需要我们操心,系统对于线程的创建,调度管理和释放都做得很好。而 NSThread
需要我们自己去管理线程的生命周期,还要考虑线程同步、加锁问题,造成一些性能上的开销
九、GCD---队列
iOS 中,有 GCD、NSOperation、NSThread 等几种多线程技术方案。
而 GCD 共有三种队列类型:
main queue:通过 dispatch_get_main_queue()获得,这是一个与主线程相关的串行队列。
global queue:全局队列是并发队列,由整个进程共享。存在着高、中、低三种优先级的全局队列。调用
dispath_get_global_queue 并传入优先级来访问队列。
自定义队列:通过函数 dispatch_queue_create 创建的队列
十、死锁
死锁就是队列引起的循环等待
1.一个比较常见的死锁例子:主队列同步
2.同样,下边的代码也会造成死锁:
十一GCD 任务执行顺序
1、串行队列先异步后同步
十二、dispatch_barrier_async
1、问:怎么用 GCD 实现多读单写?
多读单写的意思就是:可以多个读者同时读取数据,而在读的时候,不能取写入数据。并且,在写的过程
中,不能有其他写者去写。即读者之间是并发的,写者与读者或其他写者是互斥的。
这里的写处理就是通过栅栏的形式去写。
就可以用 dispatch_barrier_sync(栅栏函数)去实现
2、dispatch_barrier_sync 的用法:
这里的 dispatch_barrier_sync 上的队列要和需要阻塞的任务在同一队列上,否则是无效的。
从打印上看,任务 0-9 和任务任务 10-19 因为是异步并发的原因,彼此是无序的。而由于栅栏函数的存在,
导致顺序必然是先执行任务 0-9,再执行栅栏函数,再去执行任务 10-19。
- dispatch_barrier_sync: Submits a barrier block object for execution and waits until that
block completes.(提交一个栅栏函数在执行中,它会等待栅栏函数执行完)
- dispatch_barrier_async: Submits a barrier block for asynchronous execution and returns
immediately.(提交一个栅栏函数在异步执行中,它会立马返回)
而 dispatch_barrier_sync 和 dispatch_barrier_async 的区别也就在于会不会阻塞当前线程
比如,上述代码如果在 dispatch_barrier_async 后随便加一条打印,则会先去执行该打印,再去执
行任务 0-9 和栅栏函数;而如果是 dispatch_barrier_sync,则会在任务 0-9 和栅栏函数后去执行这
条打印。
3、则可以这样设计多读单写:
十三、dispatch_group_async
十四、Dispatch Semaphore
GCD 中的信号量是指 Dispatch Semaphore,是持有计数的信号。
Dispatch Semaphore 提供了三个函数
1.dispatch_semaphore_create:创建一个 Semaphore 并初始化信号的总量
2.dispatch_semaphore_signal:发送一个信号,让信号总量加 1
3.dispatch_semaphore_wait:可以使总信号量减 1,当信号总量为 0 时就会一直等待(阻塞所在线程),否
则就可以正常执行。
Dispatch Semaphore 在实际开发中主要用于:
-
保持线程同步,将异步执行任务转换为同步执行任务
-
保证线程安全,为线程加锁
1.保持线程同步:
2、保证线程安全,为线程加锁:
原因如下:
在子线程中并发执行 asyncTask,那么第一个添加到并发队列里的,会将信号量减 1,此时信号量等于 0,
可以执行接下来的任务。而并发队列中其他任务,由于此时信号量不等于 0,必须等当前正在执行的任务
执行完毕后调用 dispatch_semaphore_signal 将信号量加 1,才可以继续执行接下来的任务,以此类推,从而
达到线程加锁的目的。
十五、延时函数(dispatch_after)
十六、使用 dispatch_once 实现单例
十七、NSOperationQueue 的优点
NSOperation、NSOperationQueue 是苹果提供给我们的一套多线程解决方案。实际上 NSOperation、
NSOperationQueue 是基于 GCD 更高一层的封装,完全面向对象。但是比 GCD 更简单易用、代码可读性
也更高。
1、可以添加任务依赖,方便控制执行顺序
2、可以设定操作执行的优先级
3、任务执行状态控制:isReady,isExecuting,isFinished,isCancelled
如果只是重写 NSOperation 的 main 方法,由底层控制变更任务执行及完成状态,以及任务退出
如果重写了 NSOperation 的 start 方法,自行控制任务状态
系统通过 KVO 的方式移除 isFinished==YES 的 NSOperation
3、可以设置最大并发量
十八、NSOperation 和 NSOperationQueue
操作(Operation):
执行操作的意思,换句话说就是你在线程中执行的那段代码。
在 GCD 中是放在 block 中的。在 NSOperation 中,使用 NSOperation 子类
NSInvocationOperation、
NSBlockOperation,或者自定义子类来封装操作。
操作队列(Operation Queues):
这里的队列指操作队列,即用来存放操作的队列。不同于 GCD 中的调度队列
FIFO(先进先出)的原则。
NSOperationQueue 对于添加到队列中的操作,首先进入准备就绪的状态(就绪状态取决于操作之间的依赖
关系),然后进入就绪状态的操作的开始执行顺序(非结束执行顺序)由操作之间相对的优先级决定(优
先级是操作对象自身的属性)。操作队列通过设置最大并发操作数(maxConcurrentOperationCount)来控制并发、串行。
NSOperationQueue 为我们提供了两种不同类型的队列:主队列和自定义队列。主队列运行在主线程之上,
而自定义队列在后台执行。
十九、NSThread+runloop 实现常驻线程
NSThread 在实际开发中比较常用到的场景就是去实现常驻线程。
- 由于每次开辟子线程都会消耗 cpu,在需要频繁使用子线程的情况下,频繁开辟子线程会消耗大量的
cpu,而且创建线程都是任务执行完成之后也就释放了,不能再次利用,那么如何创建一个线程可以
让它可以再次工作呢?也就是创建一个常驻线程。
首先常驻线程既然是常驻,那么我们可以用 GCD 实现一个单例来保存 NSThread
二十、自旋锁与互斥锁
自旋锁:
是一种用于保护多线程共享资源的锁,与一般互斥锁(mutex)不同之处在于当自旋锁尝试获取锁时以忙等
待(busy waiting)的形式不断地循环检查锁是否可用。当上一个线程的任务没有执行完毕的时候(被锁住),
那么下一个线程会一直等待(不会睡眠),当上一个线程的任务执行完毕,下一个线程会立即执行。
在多 CPU 的环境中,对持有锁较短的程序来说,使用自旋锁代替一般的互斥锁往往能够提高程序的性能。
互斥锁:
当上一个线程的任务没有执行完毕的时候(被锁住),那么下一个线程会进入睡眠状态等待任务执行完毕,
当上一个线程的任务执行完毕,下一个线程会自动唤醒然后执行任务。
总结:
自旋锁会忙等: 所谓忙等,即在访问被锁资源时,调用者线程不会休眠,而是不停循环在那里,直到被锁
资源释放锁。
互斥锁会休眠: 所谓休眠,即在访问被锁资源时,调用者线程会休眠,此时 cpu 可以调度其他线程工
作。直到被锁资源释放锁。此时会唤醒休眠线程。
优缺点:自旋锁的优点在于,因为自旋锁不会引起调用者睡眠,所以不会进行线程调度,CPU 时间片轮转等耗时 操作。所有如果能在很短的时间内获得锁,自旋锁的效率远高于互斥锁。
缺点在于,自旋锁一直占用 CPU,他在未获得锁的情况下,一直运行--自旋,所以占用着 CPU,如果不
能在很短的时 间内获得锁,这无疑会使 CPU 效率降低。自旋锁不能实现递归调用。
-
*自旋锁:atomic、OSSpinLock、dispatch_semaphore_t
-
互斥锁:pthread_mutex、@ synchronized、NSLock、NSConditionLock 、NSCondition、NSRecursiveLock*
总结
作品不易,觉得可以的话,就麻烦大家关注,点赞一下!**作为常在各种网站的游荡的搬运工,也收集了许多iOS开发的资料,面试资料,也白嫖到免费直播链接,创建了自己的ios开发交流圈,去要资料可点击领取资料