## 1.`NSThread`相关知识
## 2.`GCD` 相关知识?(栅栏函数、Group、定时器、信号量、队列类型、任务派发方式、快速迭代、延迟处理)
##### 1.栅栏函数(控制任务的执行顺序)
“`objc
dispatch_barrier_async(queue, ^{
NSLog(@”barrier”);
});
“`
##### 2.延迟执行(延迟·控制在哪个线程执行)
“`objc
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@”—%@”,[NSThread currentThread]);
});
“`
##### 3.一次性代码
“`objc
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@”—–“);
});
“`
##### 4.快速迭代(开多个线程并发完成迭代操作)
“`objc
dispatch_apply(subpaths.count, queue, ^(size_t index) {
});
“`
##### 5.队列组(同栅栏函数)
“`objc
dispatch_group_t group = dispatch_group_create();
// 队列组中的任务执行完毕之后,执行该函数
dispatch_group_notify(dispatch_group_t group,dispatch_queue_t queue,dispatch_block_t block);
// 进入群组和离开群组
dispatch_group_enter(group);//执行该函数后,后面异步执行的block会被gruop监听
dispatch_group_leave(group);//异步block中,所有的任务都执行完毕,最后离开群组
//注意:dispatch_group_enter|dispatch_group_leave必须成对使用
“`
##### 6.信号量(并发编程中很有用)
## 3.`NSOperation` 和 `NSOperationQueue`相关知识?
(最大并发数、线程依赖)
## 4.如何实现线性编程?
信号量、栅栏、Dispatch_group
## 5.说一下 `GCD` 并发队列实现机制?
利用的时间片轮转
## 6.`NSLock`
> `NSLock` 遵循 `NSLocking` 协议,`lock` 方法是加锁,`unlock` 是解锁,`tryLock` 是尝试加锁,如果失败的话返回 NO,`lockBeforeDate:` 是在指定 `Date` 之前尝试加锁,如果在指定时间之前都不能加锁,则返回 `NO`。
“`objc
@protocol NSLocking
– (void)lock;
– (void)unlock;
@end
@interface NSLock : NSObject <NSLocking> {
@private
void *_priv;
}
– (BOOL)tryLock;
– (BOOL)lockBeforeDate:(NSDate *)limit;
@property (nullable, copy) NSString *name NS_AVAILABLE(10_5, 2_0);
@end
“`
## 7.`NSContion`
“`objc
@interface NSCondition : NSObject <NSLocking> {
@private
void *_priv;
}
– (void)wait;
– (BOOL)waitUntilDate:(NSDate *)limit;
– (void)signal;
– (void)broadcast;
@property (nullable, copy) NSString *name NS_AVAILABLE(10_5, 2_0);
@end
“`
`NSCondition` 的对象实际上作为一个锁和一个线程检查器,锁上之后其它线程也能上锁,而之后可以根据条件决定是否继续运行线程,即线程是否要进入 `waiting` 状态,经测试,直接进入 `waiting` 状态,当其它线程中的该锁执行 `signal` 或者 `broadcast` 方法时,线程被唤醒,继续运行之后的方法。
## 8.条件锁 – `NSContionLock`
“`objc
@interface NSConditionLock : NSObject <NSLocking> {
@private
void *_priv;
}
– (instancetype)initWithCondition:(NSInteger)condition NS_DESIGNATED_INITIALIZER;
@property (readonly) NSInteger condition;
– (void)lockWhenCondition:(NSInteger)condition;
– (BOOL)tryLock;
– (BOOL)tryLockWhenCondition:(NSInteger)condition;
– (void)unlockWithCondition:(NSInteger)condition;
– (BOOL)lockBeforeDate:(NSDate *)limit;
– (BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit;
@property (nullable, copy) NSString *name NS_AVAILABLE(10_5, 2_0);
@end
“`
`NSConditionLock` 和 `NSLock` 类似,都遵循 `NSLocking` 协议,方法都类似,只是多了一个 `condition` 属性,以及每个操作都多了一个关于 `condition` 属性的方法,例如 `tryLock`,`tryLockWhenCondition:`,`NSConditionLock` 可以称为条件锁,只有 `condition` 参数与初始化时候的 `condition` 相等,`lock` 才能正确进行加锁操作。而 `unlockWithCondition:` 并不是当 `Condition` 符合条件时才解锁,而是解锁之后,修改 `Condition` 的值。
## 9.递归锁 – `NSRecursiveLock`
“`objc
@interface NSRecursiveLock : NSObject <NSLocking> {
@private
void *_priv;
}
– (BOOL)tryLock;
– (BOOL)lockBeforeDate:(NSDate *)limit;
@property (nullable, copy) NSString *name NS_AVAILABLE(10_5, 2_0);
@end
NSRecursiveLock 是递归锁,他和 NSLock 的区别在于,NSRecursiveLock 可以在一个线程中重复加锁(反正单线程内任务是按顺序执行的,不会出现资源竞争问题),NSRecursiveLock 会记录上锁和解锁的次数,当二者平衡的时候,才会释放锁,其它线程才可以上锁成功。
“`
## 10.同步锁 – `Synchronized(self) {// code}`
`@synchronized(object)` 指令使用的 `object` 为该锁的唯一标识,只有当标识相同时,才满足互斥。
`@synchronized(object)` 简化了加锁的行为,我们不在需要显示的加锁。
##11.信号量 – `dispatch_semaphore`。
> 如果获取不到 `锁` ,会将当前线程阻塞、休眠,直到其他线程释放 `锁` 时,会唤醒当前线程。
## 12.自旋锁 – `OSSpinLock`
> 自旋锁是一种 “忙等” 的锁,它适用于轻量访问,譬如在 `引用计数表` 和 原子性`atomic`。
如果当前线程的 `锁` 被其他线程获取,当前线程会不断探测 `锁` 是否有被释放,如果检测出释放,会第一时间获取这个锁
## 13.互斥锁 – `pthread_mutex`
“`objc
// 初始化方法()
int pthread_mutex_init(pthread_mutex_t * __restrict, const pthread_mutexattr_t * __restrict);
// pthread_mutex_t * __restrict 代表互斥锁的类型,有以下四种
1.PTHREAD_MUTEX_NORMAL 缺省类型,也就是普通锁。当一个线程加锁以后,其余请求锁的线程将形成一个等待队列,并在解锁后先进先出原则获得锁。
2.PTHREAD_MUTEX_ERRORCHECK 检错锁,如果同一个线程请求同一个锁,则返回 EDEADLK,否则与普通锁类型动作相同。这样就保证当不允许多次加锁时不会出现嵌套情况下的死锁。
3.PTHREAD_MUTEX_RECURSIVE 递归锁,允许同一个线程对同一个锁成功获得多次,并通过多次 unlock 解锁。
4.PTHREAD_MUTEX_DEFAULT 适应锁,动作最简单的锁类型,仅等待解锁后重新竞争,没有等待队列。
“`
“`objc
// 常用的一些方法
int pthread_mutex_lock(pthread_mutex_t *);
int pthread_mutex_trylock(pthread_mutex_t *);
int pthread_mutex_unlock(pthread_mutex_t *);
int pthread_mutex_destroy(pthread_mutex_t *);
int pthread_mutex_setprioceiling(pthread_mutex_t * __restrict, int,int * __restrict);
int pthread_mutex_getprioceiling(const pthread_mutex_t * __restrict,int * __restrict);
“`
###### 如何利用 `pthread_mutex` 创建一个递归锁
“`objc
static pthread_mutex_t theLock;
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&theLock, &attr);
“`
## 14.分步锁 – `NSDistributedLock`。
在 引用计数表的数据结构里,一张 `sideTable` 表利用 `分离锁` 被分成了多个部分。
这样可以对一张表的多个部分,同时进行操作,提升了效率。
## 15.如何确保线程安全?
* 采用 `串行队列`。
* 加上 `同步锁`。
## 16.`NSMutableArray`、和 `NSMutableDictionary`是线程安全的吗?`NSCache`呢?
> 在做缓存时,优先使用 `NSCache` 而不是 `NSDictionary` ,我们熟悉的框架 `SDWebimage` 就是采用的 `NSCache`。
`NSCache` 优点如下:
1. 系统资源将要耗尽时,它可以自动删减缓存。
2. 可以设置最大缓存数量。
3. 可以设置最大占用内存值。
4. `NSCache` 线程是安全的。
## 17.多线程的 `并行` 和 `并发` 有什么区别?
并行:充分利用计算机的多核,在多个线程上同步进行
并发:在一条线程上通过快速切换,让人感觉在同步进行
## 18.多线程有哪些优缺点?
##### 创建线程是需要花费资源的
– 一条主线程占用1M,一条子线程占用 512Kb。
– 线程的切换也是需要花费资源的
– 优点就是提升效率,充分利用了计算机的多核特性。
## 19.如何自定义 `NSOperation` ?
需要重写其 `main` 方法
## 20.`GCD` 与 `NSOperationQueue` 有哪些异同?
* 1> `GCD` 是纯 `C` 语言的 `API` 。`NSOperationQueue` 是基于 `GCD` 的 `OC` 的封装。
* 2> `GCD` 只支持 `FIFO` 队列,`NSOperationQueue` 可以方便设置执行顺序,设置最大的并发数量。
* 3> `NSOperationQueue` 可是方便的设置 `operation` 之间的依赖关系,`GCD` 则需要很多代码。
* 4> `NSOperationQueue` 支持 `KVO`,可以检测 `operation` 是否正在执行`(isExecuted)`,是否结束`(isFinished)`,是否取消`(isCanceled)`
* 5>GCD的执行速度比NSOperationQueue快。
#### 使用场合:
* 任务之间不太相互依赖:`GCD`
* 任务之间有依赖或要监听任务的执行情况:`NSOperationQueue`
## 21.解释一下多线程中的死锁?
死锁是由于多个线程(进程)在执行过程中,因为争夺资源而造成的互相等待现象,你可以理解为卡主了。产生死锁的必要条件有四个:
– 互斥条件 : 指进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程占用。如果此时还有其它进程请求资源,则请求者只能等待,直至占有资源的进程用毕释放。
– 请求和保持条件 : 指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞,但又对自己已获得的其它资源保持不放。
– 不可剥夺条件 : 指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放。
– 环路等待条件 : 指在发生死锁时,必然存在一个进程——资源的环形链,即进程集合{P0,P1,P2,···,Pn}中的P0正在等待一个P1占用的资源;P1正在等待P2占用的资源,……,Pn正在等待已被P0占用的资源。
“`objc
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@”2″);
});
NSLog(@”1″);
// 什么也不会打印,直接报错
“`
最常见的就是 `同步函数` + `主队列` 的组合,本质是队列阻塞。
### 22.分类和类拓展的区别?
* 1.`分类` 的加载在 `运行时`,`类拓展` 的加载在 `编译时`。
* 2.`分类` 不能给系统的类添加方法。
* 3.`类拓展` 只以声明的形式存在,一般存在 .m 文件中。
******
持续更新中~
******
## **文末推荐:iOS热门文集**
* **① [BAT等各个大厂iOS面试真题+答案大全](https://github.com/iOS-Mayday/heji)**
* **② [iOS中高级开发必看的热门书籍(经典必看)](https://github.com/iOS-Mayday/iOSAdvanced-Roadmap)**
* **③ [iOS开发高级面试”简历制作”指导](https://github.com/iOS-Mayday/Interview-questions-iOS)**
* **④ [iOS面试流程到基础知识大全](https://github.com/iOS-Mayday/iOS-Interview-Strategy)**