iOS面试复习
线程保活的方式
在常规情况下,线程的生命周期与任务的执行时间相关联。一旦任务执行完毕,线程就会自动退出。但是,有些场景下需要保持线程一直处于活动状态,等待新的任务到来,以提高性能或满足特定需求。
如果开发中需要经常在子线程中执行任务,那么频繁的创建和销毁线程就会造成资源的浪费,这不符合我们的初衷。 此时就需要我们对线程进行保活,保证线程在应该处理事情的时候醒来,空闲的时候休眠。
-
runloop保活 -
GCD保活,使用dispatch_semaphore信号量机制,通过阻塞线程实现保活
缓存NSCache
NSCache是 Foundation 框架中提供的一个用于临时存储键值对数据的类,它类似于NSMutableDictionary,但具有自动管理内存和线程安全的特性。NSCache 通常用于缓存临时数据,例如图像、数据对象等,以提高性能和响应速度
锁
NSLock互斥锁atomtic读写锁(自旋锁)synchronized{}互斥锁,性能较差dispatch_semaphore信号量机制NSRecursiveLock递归锁,锁多次 -> 解多次
计时
NSTime有精度问题,受runLoop模式影响CADisplayLink基于屏幕刷新率的计时器GCD延时操作,GCD定时器,使用系统时钟UIView动画时间NSDate使用系统时钟
多线程
NSOperation,线程依赖、开始、暂停、中止GCD强调队列和执行,有一次性执行函数、延迟执行函数、栅栏函数、信号量机制函数、Group组NSOperation和GCD侧重点不同,性能上无谁更优一说
卡顿检测
Xcode Instrument工具检测runLoop状态转化卡顿检测FPS检测
崩溃检测
bugly 检测崩溃,收集崩溃信息
野指针和僵尸对象
- 野指针,指向未做初始化或已经释放的指针
- 僵尸对象,野指针指向的对象
MyObject *obj = [[MyObject alloc] init];
[obj release]; // 释放对象
[obj doSomething]; // 使用已释放的对象,成为野指针
属性的修饰符
@public 公开
@protected 受保护,内部和子类可以访问
@private 内部可访问
KVC和KVO
- 使用
KVC的调用顺序
// 正常情况下
get/set方法 -> _属性 -> 属性
// 报错调用方法
value for Undefinekey
// nil调用方法
set nil value for Key
// 无效调用方法
set Validate Value for Key Error
- 使用
KVO
添加观察者 addObserver -> 通过观察者的代理方法监听 -> delloc的时候记得remove
UITableView的优化
Cell重用- 高度提前计算
- 使用懒加载
- 多线程下载
reloadItem- 使用预加载的代理方法
- 减少层级结构
下载任务同步
先无序下载5个 A任务,最后下载 B任务
GCD异步串行队列GCD dispatch_semaphore信号量机制GCD栅栏函数GCD Group组NSOperation依赖
init、initialize、load
load 加载类到内存 -> initialize 第一次发消息 -> init 每个对象
load:加载代码段,类到内存initialize:类方法,类加载时初始化init:每个对象满足特定初始化需求
load 可以在这里使用 runtime 交换方法,修改类的实现,全局配置,但是也应该避免阻塞和耗时操作
数据库
SQLite:注意多线程安全问题,性能优化可以考虑使用索引,合理设计,减少查询量,使用GCD队列、锁CoreData:对象特性,比较复杂,是对SQLite的高级封装,支持多线程,管理内存,异常处理,兼容性,适合大数据管理FMDB:简单封装SQLite,拥有事务(多个SQL语句,提交一次),多线程(FMDatabaseQueue队列,使用GCD),异常回滚
离屏渲染
在内存占据一块区域进行绘制,阻碍主线程,后更新到UI上面,不是实时渲染,iOS 中常见的离屏渲染发生在阴影、 mask 、毛玻璃效果上
归档解档
- 对象转为二进制数据文件
xxx.archive - 遵守
NSCodeing协议 - 实现
encodeWithCoder和initWithCoder方法,在内部做手动属性存储
关联对象
本质是对象内存维护的一个哈希表,存储在里面
消息转发
objc_msgSend(receiver, @selector(message)),会在缓存内寻找方法,没找到则去类的方法列表查找并缓存,没有再去父类找
动画卡顿原因
- 多个复杂动画同时进行
- 大量图层操作:增删改动态变换
- 主线程阻碍
- 资源过载:
CPU、GPU、内存
内存算法
LRU算法(最近最少使用),建立一个队列链表,使用该对象则将其置顶,淘汰则淘汰队尾FIFO算法,淘汰最早那个LIFO算法,淘汰最后那个Random算法,随机算法
理解项目架构和项目优点
根据以往的项目经验举例分析
算法
插入排序
- 将待排序的元素逐个插入已排序序列的合适位置,从而逐步构建有序序列
- 平均时间复杂度
O(n^2),最快n-1
// 前面为排序完成,后面为未排序
49 38 65 97 76
// 第一次:
38 49|65 97 76
// 第二次:
38 49 65|97 76
// 第三次:
38 49 65 97|76
// 第四次:排序完成
38 49 65 76 97|
折半插入排序
- 插入排序算法的基础上进行改进的一种排序算法
- 它通过利用二分查找的思想来寻找插入位置
- 平均时间复杂度
O(n^2)
冒泡排序
- 通过不断比较相邻的两个元素并交换它们的位置,使得较大(或较小)的元素逐渐浮到序列的一端,从而达到排序的目的
- 平均时间复杂度
O(n^2)
// 前面为排序完成,后面为未排序
49 38 65 97 13
// 第一次:
13|49 38 65 97
// 第二次:
13 38|49 65 97
// 第三次:
13 38 49|65 97
// 第四次:排序完成
13 38 49 65|97
选择排序
- 每次从未排序的序列中选择最小(或最大)的元素,然后将其放置在已排序序列的末尾(或开头),逐步构建有序序列
- 类似冒泡排序,但是移动次数少
- 平均时间复杂度
O(n^2)
// 前面为排序完成,后面为未排序
49 38 65 97 13
// 第一次:
13|49 38 65 97
// 第二次:
13 38|49 65 97
// 第三次:
13 38 49|65 97
// 第四次:排序完成
13 38 49 65|97
快速排序
- 基于分治的思想,通过将序列划分为较小和较大的子序列,逐步将子序列排序,最终完成整个序列的排序
- 平均时间复杂度
O(nlog2 n),最坏的情况下时间复杂度O(n^2)
// 完成一次排序的结果,把值放在正确的位置
// 38的左边都比他小,38的右边都比他大
49 13 65 97 38|38
13 38 65 97 49|
设计模式
开闭原则
开放扩展、关闭修改,强调扩展性、避免对现有代码的大量修改
MVC、MVVM
MVC 是一种经典的架构模式,由以下三个组件组成:
Model(模型):负责处理数据和业务逻辑。它封装了应用程序的数据和状态,并提供了访问和修改数据的接口。View(视图):负责用户界面的展示和用户交互。它根据模型的数据来渲染用户界面,并将用户的输入事件传递给控制器进行处理。Controller(控制器):负责处理用户输入和控制应用程序的流程。它接收来自视图的用户输入事件,更新模型的数据和状态,然后通知视图进行相应的更新。
MVC 核心思想是将应用程序分为三个组件,每个组件负责不同的职责。这样可以实现数据、逻辑和界面的分离,提高代码的可读性和可维护性。同时,MVC 持多个视图和控制器的组合,以适应不同的用户界面需求。
MVVM 一种在 MVC 基础上演化而来的架构模式,由以下三个组件组成:
Model(模型):与MVC中的模型相同,负责处理数据和业务逻辑。View(视图):与MVC中的视图相同,负责用户界面的展示和用户交互。ViewModel(视图模型):负责将模型的数据适配为视图可以使用的形式,并包含了视图的状态和操作。它充当了视图和模型之间的桥梁,将模型的数据绑定到视图上,同时将视图的操作反馈到模型上。
MVVM 的核心思想是引入了视图模型,将视图与模型的直接交互解耦,通过数据绑定的方式实现它们之间的关联。这样可以使视图和模型更加独立,提高代码的可测试性和可维护性。MVVM 也广泛应用于现代的前端开发框架,例如Angular 和Vue.js 。
总的来说,MVC 和 MVVM 都是常见的软件架构模式,用于组织代码和分离关注点。MVC 适用于传统的应用程序开发,而 MVVM 更适用于现代的前端开发和数据驱动的应用程序。选择合适的模式取决于具体的
中介者模式
它用于解耦各个对象之间的复杂交互关系。中介者模式通过引入一个中介者对象,将对象之间的直接通信转变为通过中介者进行的间接通信。
外观模式
它提供了一个简化复杂系统的接口。外观模式通过隐藏系统内部的复杂性,为客户端提供一个简单的接口,使得客户端可以更容易地使用系统。然而,外观模式也有一些限制。它可能会导致系统的扩展困难,因为增加新的子系统可能需要修改外观类的代码。
单例模式
单例模式下,对应类只能生成一个实例,如果事务多起来,这也容易职责过重。
+ (instancetype)shareMediator {
static ChatMediator *shareInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
shareInstance = [[ChatMediator alloc] init];
});
return shareInstance;
}
观察者模式
定义对象之间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新,一般使用 KVO 实现。
代理模式
为某个对象提供一个代理,并由这个代理对象控制对原对象的访问。
工厂模式
专门定义一个类(工厂类)来负责创建其他类的实例。可以根据创建方法的参数来返回不同类的实例,被创建的实例通常都具有共同的父类。
// 可乐抽象类
@interface Cola : NSObject
@end
// 可口可乐产品类
@interface CocaCola : Cola
@end
// 百事可乐产品类
@interface PesiCola : Cola
@end
// 简单工厂实现
@implementation SimpleFactory
+ (Cola *)createColaWithType:(NSInteger)type {
switch (type) {
case 0:
return [CocaCola new];
case 1:
return [PesiCola new];
default:
return nil;
break;
}
}
@end
适配器模式
该模式充当一个适配器,位于两个不兼容的接口之间,使它们能够协同工作。
计算机网络
GET、POST、PUT、Delete
GET查:URL存储参数,不加密,不安全,长度限制,被缓存记录,一个TCP包,一次发送,速度快POST增:参数存放在请求体,一般没有长度限制,两个TCP包,相对安全,慢一点PUT改Delete删
三次握手四次挥手
- 三次握手可以检测两端的收发功能是否正常
- 三次握手可以保证过程安全,不会因为超时/网络延迟、丢包而出现避免重复连接或者数据丢失
- 四次挥手需要等待发送端发生完毕,避免出现状态不同步问题
HTTP加密
建立连接 -> 握手验证 -> 加密通讯 -> 完整性验证
其他
图片显示过程
加载数据 -> 解码 -> 渲染 -> 显示
图片格式
常用格式
JPG:有损格式,文件小PNG:无损格式,带透明通道,文件大GIF:动画,多帧图像,带透明通道
其他
BMP:无压缩,文件大,用于计算机图形处理和图形编辑TIFF:灵活,支持多种图像类型,多帧、透明、常见印刷出版WebP:google开发,文件小,质量好,用于传输
计算图片大小
// RGB格式 3个字节
(256,256,256)
// RGBA格式 4个字节
(256,256,256,1)
256 = 2^8 = 8bit = 1Byte
1MB = 1024 * 1024 Byte
一张 1024 * 1024 的RGB图片 为 1MB * 3 = 3MB
一张 1024 * 1024 的RGBA图片 为 1MB * 4 = 4MB
MD5加密(哈希算法)
单向加密算法,无法逆向推出数据,但是可以暴力破解、碰撞💥
graph TD
数据 --> 二进制 --> 分组多个数据块 --> 位运算等 --> 16进制
CPU调用GPU
图形API如openGL、Metal
graph TD
图形API --> GPU驱动 --> GPU --> GPU驱动 --> 图形API