iOS面试的题目总结

UIView和CALlayer的关系:

UIView和CALayer都遵循单一职责设计原则,UIView为其提供内容,以及负责处理触摸等事件,参与响应链.
layer负责显示内容的contens

点击事件传递的流程:

点击屏幕时,将Touch以UIEvent的方式加入UIApplication事件队列中,UIApplication从事件队列中取出最新的触摸事件进行分发传递到UIWindow进行处理,UIWindow 会通过hitTest:withEvent:方法寻找触碰点所在的视图,视图寻找的顺序如下:

UIApplication -> UIWindow -> rootView -> subview
在顶级视图Root View 上调用pointInside:withEvent:方法判断触摸点是否在当前视图内;
如果返回NO,那么hitTest:withEvent:返回nil;
如果返回YES,那么它会向当前视图的所有子视图发送hitTest:withEvent:消息
所有子视图的遍历顺序是从最顶层视图一直到到最底层视图,即从subviews数组的末尾向前遍历,直到有子视图返回非空对象或者全部子视图遍历完毕。

UI显示过程

CPU绘制图像 绘制动画
GPU着色 图元装配 光栅化 片段着色 片段处理

UI卡顿掉帧原因

一帧画面CPU+GPU处理时长超过 16.7ms 也就是1/60秒

滚动流畅优化

CPU 层面
子线程创建对象,调整,销毁,预排版(frame计算,文本计算),图片解码 ,
预渲染(文本异步绘制,图片编解码等)

GPU层面:
纹理渲染
视图混合层级减少

UI视图异步绘制步骤

调用setNeedsDisplay时在 [CALayer display]
是否能响应 layer.delegate responds To
@selector(displayLayer:) 返回YES能响应进入绘制入口 NO 系统绘制入口

需要在方法外外面调用 [asLabel.layer setNeedsDisplay]; 不然会走系统的绘制流程
- (void)displayLayer:(CALayer *)layer{
    //异步绘制入口
}
什么离屛渲染.

在屏渲染 GPU的渲染操作在当前用于显示的屏幕缓冲区中进行
离屛渲染 GPU的渲染操作在当前屏幕缓冲区外新开辟一个缓冲区进行渲染

为何要避免离屛渲染

卡顿和掉帧,增加GPU的工作量

离屛渲染何时触发

圆角(当和maskToBounds一起使用时)
图层蒙版
阴影
开启光栅化

如何避免(蒙版和阴影给固定路径)

利用分类做哪些事情:
  1. 声明私有方法
  2. 分解体积庞大的类文件
  3. 把Framework的私有方法公开
分类中同样的方法哪一个会生效. (如何调用到你想调用的那个方法)

最后参加编译的方法会生效

分类方法移动.
会调整
分类实现原理
运行时决议,谁方法生效.决定为编译顺序,后编译覆盖先的.因为方法的查找原因.

分类能否添加成员变量

间接添加,通过关联对象方法(getAssociatedObject,setAssociatedObject,removeAssociatedObiects)

关联对象的管理

所有的关联对象全在一个AssociationsManager 通过 AssOciationsHashMap管理

一般用扩展做什么

声明私有属性
私有方法
私有成员变量

分类和扩展区别
分类:运行时决议,有声明,有实现,可以为系统类添加分类
扩展:编译时决议,只以声明的形式存在,多数情况下寄生于宿主类中,不能为系统类进行扩展

代理:

软件设计模式

传递方式一对一,通知为一对多

代理为三方:
协议: 要求代理方实现的接口( 成员属性和方法)
委托方 : 调用代理方遵从的协议方法
代理方: 按照协议实现方法

通知:

观察者模式 实现跨层传递消息
传递为一对多

KVO

Key-value observing 的缩写,对观察者模式的又一实现
手动实现kvo 采用isa混写(isa-swizzling)来实现KVO
willchangevalueforkey
didchangevalueforkey

copy关键字

可变对象的copy和mutableCopy都是深拷贝。
不可变对象的copy是浅拷贝,mutableCopy是深拷贝。
copy方法返回的都是不可变对象

NSMutableArray --> copy— NSArray(深拷贝)
NSMutableArray --> mutableCopy — NSMutableArray(深拷贝)

NSArray --> copy— NSArray(浅拷贝)
NSArray --> mutableCopy— NSMutableArray(浅拷贝)

内存数据结构

isa指针:
32位:isa代表class的地址
64位:isa值的部分代表class的地址

关于对象:
isa指向类对象
关于类对象:
isa指向元类对象

内存区数据存储位置:
代码段在低低址值,内核区在高地址值. 堆里面的数据从低往高分布,栈里面的数据从高往低分布.如下图
image.png

内存布局:

栈区(stack):存储方法调用
堆区(heap):通过alloc等分配的对象
未初始化数据(bss):未初始化的全局变量等
data:已初始化的全局变量等
text:程序代码

TaggedPointer 技术
64位以后isa是一个联合体+位域的结构,联合体union内部成员为互斥存在.

  uintptr_t nonpointer        : 1;                                       
        uintptr_t has_assoc         : 1;                                       
        uintptr_t has_cxx_dtor      : 1;                                       
        uintptr_t shiftcls          : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ 
        uintptr_t magic             : 6;                                       
        uintptr_t weakly_referenced : 1;                                       
        uintptr_t unused            : 1;                                       
        uintptr_t has_sidetable_rc  : 1;                                       
        uintptr_t extra_rc          : 19

-nonpointer: 是否开启NONPOINTER isa指针优化;
-has_assoc: 对象是否含有关联引用
-has_cxx_dtor:对象是否含有 C++ 或者 Objc 的析构器
-shiftcls: 类的指针(重点)(arm64:33位,x86_64:44位)
-magic: 对象是否初始化完成 (arm64:0x16 ,x86_64:0x3b)
-weakly_referenced:是否为弱引用的对象
-deallocating:对象是否正在执行析构函数(是否在释放内存)
-has_sidetable_rc:判断是否需要用sidetable去处理引用计数,(extra_rc的大小影响到这个变量)
-extra_rc: 存储该对象的引用计数值减一后的结果

__block破解循环引用:
MRC下,__block修饰对象不会增加其引1用计数,避免了循环引用。
ARC下,__block修饰对象会被强引用,无法避免循环引用,需手动解环。

什么是ARC?

ARC就是自动的引用计数,顾名思义,是自动帮我们填写引用计数代码的一项功能,重写了set方法和dealloc方法.简单地理解ARC,就是通过指定的语法,让编译器(LLVM 3.0)在编译代码时,自动生成实例的引用计数管理部分代码,它只是一种代码静态分析(Static Analyzer)工具.

实现weak后,为什么对象释放后会自动为nil

runtime 对注册的类, 会进行布局,对于 weak 对象会放入一个 hash 表中。 用 weak 指向的对象内存地址作为 key,当此对象的引用计数为 0 的时候会 dealloc,假如 weak 指向的对象内存地址是 a ,那么就会以 a 为键, 在这个 weak 表中搜索,找到所有以 a 为键的 weak 对象,从而设置为 nil 。
当weak指向的对象被释放时,处理weak指针的过程如下:
1.调用objc_release
2.因为对象的引用计数为0,所以执行dealloc
3.在dealloc中,调用了_objc_rootDealloc函数
4.在_objc_rootDealloc中,调用了object_dispose函数
5.调用objc_destructInstance
6.最后调用objc_clear_deallocating,详细过程如下:
- 从weak表中获取废弃对象的地址为键值的记录
- 将包含在记录中的所有附有 weak修饰符变量的地址,赋值为 nil
- 将 weak表中该记录删除
- 从引用计数表中删除废弃对象的地址为键值的记录

苹果是如何实现AutoreleasePool的

首先,AutoreleasePool是以链表的形式保存在内存中的.主要底层数据结构是:__AtAutoreleasePool、AutoreleasePoolPage.
每个AutoreleasePoolPage对象占用4096个字节,在内存中的形式如下:
image.png

1.每一个AutoreleasePoolPage都有一个parent、child指针指向上一个和下一个.
2.id *next指向了下一个能存放autorelease对象地址的区域
3.调用push方法会将一个POOL_BOUNDARY(哨兵对象)入栈,并且返回其存放的内存地址
4.调用pop方法时传入一个POOL_BOUNDARY的内存地址,会从最后一个入栈的对象开始发送release消息,直到遇到这个POOL_BOUNDARY
5.POOL_BOUNDARY 就是哨兵对象,它是一个宏,值为nil,标志着一个自动释放池的边界。
6.autoreleasepoolpage 是以双向链表的形式连接起来的,只有新建一个pool会执行push操作,这时候会在page中插入一个POOL_BOUNDARY,并不是每页都插

什么是循环引用?你遇到过哪些循环引用,是怎样解决的?
  1. 相互持有情况下weak解决
  2. block情况下 __weak解决
  3. NSTimer可以使用第三方对象解决,或者不使用scheduledTimerWithTimeInterval,改用timerWithTimeInterval方法,
什么是Block

block是将函数及其执行上下文封装起来的对象

多线程:

NSOperation实现以下方案:
需要和NSOperationQueue配合使用来实现多线程方案
任务执行状态控制:

isReady 当前任务是否处于就绪状态
isExecuting 当前任务是否处于正在执行中状态
isFinished 当前任务是否已执行完毕
isCancelled 当前任务是否已取消

最大并发量控制:
queue.maxConcurrentOperationCount = 2;
任务依赖:

 // 创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc]init];
    // 需要操作对象
    NSBlockOperation *op0 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"登录...");
    }];
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        [NSThread sleepForTimeInterval:2];
        NSLog(@"扣费...");
    }];
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"下载文件...");
    }];
    // 添加依赖 -- 添加依赖的时候要注册不要循环依赖
    // 添加依赖的时候,条件要充分,
    [op1 addDependency:op0];
    [op2 addDependency:op1];
    op2.completionBlock = ^{
        NSLog(@"所有的都搞完了");
    };
    // 把所有的操作添加到队列
    [queue addOperations:@[op0, op1, op2] waitUntilFinished:NO];

NSOperation:
1.如果只重写main方法 ,底层控制变更任务执行完成状态,以及任务退出。
2.如果重写了star方法,自行控制任务状态

系统是怎样移除—个isFinished=YES的NSOperation的?
通过KVO来监听的.

NSThread:封装C语言的pthread,流程如下:
start()->创建pthread->main()->[target performSelector:selector]->exit()

怎样用GCD实现多读单写?
栅栏异步:
//异步栅栏调用设置数据
dispatch_barrier_async (concurrent_queue, ^{
[userCenterDic setObiect:obj forKey:key];
});

ioS系统为我们提供的几种多线程技术各自的特点是怎样的?

  1. pthread
  2. NSThread
  3. GCD
  4. NSOperation

关于锁的了解:
自旋锁: 等待锁的线程会处于忙等(busy-wait)状态,一直占用着CPU资源

  1. 目前已经不再安全,可能会出现优先级反转问题
  2. 如果等待锁的线程优先级较高,它会一直占用着CPU资源,优先级低的线程就无法释放锁
  3. 需要导入头文件#import <libkern/OSAtomic.h>

互斥锁: 从底层调用看,等待os_unfair_lock锁的线程会处于休眠状态,并非忙等

  1. 需要导入头文件#import <os/lock.h>
  2. 用于取代不安全的OSSpinLock ,从iOS10开始才支持

递归锁: 递归锁有一个特点,就是同一个线程可以加锁N次而不会引发死锁。但是需要成对出现

Foundation系列:
NSLock是对mutex普通锁的封装,NSRecursiveLock也是对mutex递归锁的封装,API跟NSLock基本一致,NSCondition是对mutex和cond的封装,NSConditionLock是对NSCondition的进一步封装,可以设置具体的条件值

你都用过哪些锁(线程同步方案)?结合实际谈谈你是怎样使用的?
性能从高到低:
os_unfair_lock(互斥锁)
OSSpinLock(自旋锁)
dispatch_semaphore(信号量,设置为1代表只允许1条线程访问资源,保证线程同步)
pthread_mutex(互斥锁,等待锁的线程会处于休眠状态.#import <pthread.h>)
dispatch_queue(DISPATCH_QUEUE_SERIAL)(串行队列,实现线程同步)
NSLock
NSCondition
pthread_mutex(recursive)
NSRecursiveLock
NSConditionLock
@synchronized(是对mutex递归锁的封装)

HTTP相关

1.请求报文

①是请求方法,GET和POST是最常见的HTTP方法,除此以外还包括DELETE、HEAD、OPTIONS、PUT、TRACE。
②为请求对应的URL地址,它和报文头的Host属性组成完整的请求URL。
③是协议名称及版本号。
请求头:
④是HTTP的报文头,报文头包含若干个属性,格式为“属性名:属性值”,服务端据此获取客户端的信息。
与缓存相关的规则信息,均包含在header中
请求体:
⑤是报文体,它将一个页面表单中的组件值通过param1=value1&param2=value2的键值对形式编码成一个格式化串,它承载多个请求参数的数据。不但报文体可以传递请求参数,请求URL也可以通过类似于“/chapter15/user.html? param1=value1&param2=value2”的方式传递请求参数。

2.响应报文
image.png

响应报文:
①报文协议及版本;
②状态码及状态描述;
响应头:
③响应报文头,也是由多个属性组成;
响应体:
④响应报文体,即我们真正要的“干货”

3.GET和POST区别
参数:
GET是拼接到URL后面的,POST参数是在Body里面
GET参数限制2048个字符,POST一般没有该限制
GET请求不安全,POST请求比较安全

GET:获取资源
安全的、幂等的、可缓存的
POST:处理资源
非安全的、非幂等的、不可缓存的

安全性:不应该引起server端的任何状态变化。常见的安全性的方式:GET, HEAD, OPTIONS;
幂等性:同一个请求方法执行多次和执行一次的效果完全相同 PUT,DELETE
可缓存性:请求是否可以被缓存(GET, HEAD)

TCP和UDP:
TCP,传输控制协议,可靠传输,无差错(超时重传),不丢失(丢弃丢失的),不重复(丢弃重复),按序到达(序号增大)
UDP,用户数报协议

UDP面向报文,即不拆分,也不合并

下载:http速度的增长.
慢开始->指数规律增长->拥塞避免,加法增大->网络拥塞->乘法减小->慢开始

DNS解析:
域名到IP地址的映射,DNS解析请求采用UDP数据报,且明文
两种方法:递归查询和迭代查询(知道哪个DNS服务器可能知道)

DNS劫持与HTTP的关系是怎样的?
没有关系,DNS解析发生在HTTP建立连接之前,DNS解析请求使用UDP数据报,端口号53

避免DNS劫持:
使用httpDNS:使用DNS协议向DNS服务器的53端口进行请求->使用HTTP协议向DNS服务器的80端口进行请求
长连接

Cookie:
怎样删除Cookie?
•新cookie覆盖1日cookie
•覆盖规则:name、path、domain等需要与原cookie一致
•设置cookie的expires=过去的一个时间点,或者maxAge=0

保证Cookie安全:
1.对Cookie进行加密
2.只在https上携带Cookie
3.设置Cookie为httpOnly,防止跨站脚本攻击

Session也是用来记录用户状态,区分用户的;状态存放在服务器端。

设计模式

设计模式:

  1. 责任链
  2. 桥接
  3. 适配器
  4. 单例
  5. 命今(行为参数化,隆低代码重合度)

六大设计原则:

  1. 单一职责原则(UIView和CAlayer)
  2. 开闭原则(修改关闭,扩展开放即增加方法)
  3. 接口隔离原则(使用多个专门的协议,而并非一个庞大臃肿的协议,协议的方法尽量少)
  4. 依赖倒置原则(抽象不依赖具体实现,具体实现可以依赖抽象)
  5. 里氏替換原则(父类可以被子类无缝替换,且原有功能不受任何影响:如KVO)
  6. 迪米特法则(一个对象应对其他对象尽可能少了解,高聚合,低耦合)

责任链:
App点击处理的原则

架构框架

图片缓存器的封装需要考虑的问题:

  1. 存储的Size(各种图片尺寸存储多少张)
  2. 淘汰策略(先进先出或LRU算法多久未使用)
  3. 存储方式以及大小
  4. 图片请求的最大并发量
  5. 请求超时策略
  6. 优先级
  7. 在哪个阶段进行解码(磁盘读取后或网络请求返回后 )

阅读时长框架: 页面式(push,pop),流式(朋友圈),自定义式(视频播放时间等)

记录管理者对数据保存:
管理统计记录数据,包含记录缓存,磁盘存储,上传器

如何降低数据的丢失率?

定期写入磁盘
每当记录条数达到某个值的时候,写入到磁盘

对于MVVM的理解:
View包含两部分:View和ViewController
ViewModel:对数据进行处理
Model:数据

RN数据流思想:打上标记,反向回到根节点.自顶向上遍历,更新需要更新的结点(等待下一次刷新时更新自己)

算法略

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SeanLink

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值