1.数据存储
详见数据存储文章
2. volatile
一个定义为 volatile
的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。
3.常驻线程
RunLoop最后部分
4.编程题
3个线程顺序打印1-100
- (void)print0_100 {
__block int i = 0;
dispatch_queue_t queueA = dispatch_queue_create("queue a", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t queueB = dispatch_queue_create("queue b", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t queueC = dispatch_queue_create("queue c", DISPATCH_QUEUE_CONCURRENT);
NSCondition *condition = [[NSCondition alloc] init];
dispatch_async(queueA, ^{
while (1) {
[condition lock];
while (i%3 != 0) {
[condition wait];
}
if (i > 100) {
[condition unlock];
return;
}
NSLog(@"A ==== i = %d",i);
I++;
[condition broadcast];
[condition unlock];
}
});
dispatch_async(queueB, ^{
while (1) {
[condition lock];
while (i%3 != 1) {
[condition wait];
}
if (i > 100) {
[condition unlock];
return;
}
NSLog(@"B ==== i = %d",i);
I++;
[condition broadcast];
[condition unlock];
}
});
dispatch_async(queueC, ^{
while (1) {
[condition lock];
while (i%3 != 2) {
[condition wait];
}
if (i > 100) {
[condition unlock];
return;
}
NSLog(@"C ==== i = %d",i);
I++;
[condition broadcast];
[condition unlock];
}
});
}
5.NSThread 任务如何在执行过程中让他终止
//监测当前线程是否被取消过,如果被取消了,则该线程退出。在线程里面检测取消的标记,然后执行退出
if ([[NSThread currentThread] isCancelled])
{
[NSThread exit];
}
6.NSOperation
是如何终止/取消任务的
正在执行的任务,NSOperation
也是不能取消的。
7.多线程,异步执行(async)一个 performSelector
会执行么?如果加上 afterDelay
呢
performSelector
会执行,afterDelay
不会执行;
原因 performSelector
只是单纯的直接调用某函数,afterDelay
是在该子线程执行一个 NSTimer
,注意一点:子线程中的 runloop
默认是没有启动的状态,要想 afterDelay
生效,要 runloop
在线程有事务的状态下跑起来,所以需要执行 [[NSRunLoop currentRunLoop] run]
。
8.isa
指针里面都存了什么,32和64位分别讲一下
在ARM 32 位的时候,isa
的类型是 Class
类型的,直接存储着实例对象或者类对象的地址;
在ARM 64 结构下,isa
的类型变成了共用体(union),使用了位域去存储更多信息。
9.NSMutableArray
实现原理
NSMutableArray
是一个类族,[[NSMutableArray new] class]
打印出来的是 __NSArrayM
。
通过 class-dump
逆向查看 __NSArrayM
里面的比较重要的成员变量:
_used
: 计数
_list
: 缓冲区指针
_size
: 缓冲区大小
_offset
: 缓冲区中的数组的第一个元素的索引
过程原理,OC 数组比 C 语言里的数组用了稍微复杂一点的环形缓冲区
环形缓冲区的特点是:在大多数情况下,除非缓冲区满了,否则不需要频繁分配内存。
并且 NSMutableArray
做到了如果我们在中间进行插入或者删除,只会移动最少的一边的元素。
来看一下 增、删、改、查。
查询:
以 -(void)objectAtIndex:(NSIteger)index;
为例子
-(void)objectAtIndex:(NSIteger)index
{
NSIteger originalOffset = _offset + index;
NSIteger reallOffset = originalOffset - ( _size > originalOffset ? 0 : size );
Return _list[ reallOffset ];
}
分析:分两种情况,需比较 _size
与 _offset + index
的大小
(1)A: 当 _size >= _offset + index
时:
(2)当
_size < _offset + index
时:10.自旋锁和互斥锁怎么选择
自旋锁:
- 时间短
- 加锁的代码(临界区)经常被调用,但竞争情况很少发生
- CPU资源不紧张
- 多核
互斥锁: - 时间长
- 单核
- 临界区有IO操作
- 临界区代码复杂或者循环量大
- 临界区竞争非常激烈
11.atomic
与 @synchroize
原理
atomic
原子性,setter
和 getter
加了自旋锁(实则是互斥锁)。@synchronized
是一个互斥递归锁。
什么是自旋锁呢?
锁用于解决线程争夺资源的问题,一般分为两种,自旋锁(spin)和互斥锁(mutex)。
互斥锁可以解释为线程获取锁,发现锁被占用,就向系统申请锁空闲时唤醒他并立刻休眠。互斥锁加锁的时候,等待锁的线程处于休眠状态,不会占用 CPU 的资源
自旋锁比较简单,当线程发现锁被占用时,会不断循环判断锁的状态,直到获取。自旋锁加锁的时候,等待锁的线程处于忙等状态,并且占用着 CPU 的资源。
原子操作的颗粒度最小,只限于读写,对于性能的要求很高,如果使用了互斥锁势必在切换线程上耗费大量资源。相比之下,由于读写操作耗时比较小,能够在一个时间片内完成,自旋更适合这个场景。
原来系统中自旋锁已经全部改为互斥锁实现了,只是名称一直没有更改。
为了修复优先级反转的问题,苹果也只能放弃使用自旋锁,改用优化了性能的 os_unfair_lock
,实际测试两者的效率差不多。
os_unfair_lock
用于取代不安全的 OSSpinLock
,从iOS10开始才支持
从底层调用看,等待 os_unfair_lock
锁的线程会处于休眠状态,并非忙等
12.HTTP 请求方法种类有哪些
方法 | 说明 |
---|---|
GET | 获取资源 |
POST | 传输实体主体 |
PUT | 传输文件 |
HEAD | 获得报文首部 |
DELETE | 删除文件 |
OPTION | 询问支持的方法 |
TRACE | 追踪路径 |
CONNECT | 要求用隧道协议连接代理 |
LINK | 建立和资源之间的联系 |