iOS面试复习题

480 阅读11分钟

iOS面试复习

线程保活的方式

在常规情况下,线程的生命周期与任务的执行时间相关联。一旦任务执行完毕,线程就会自动退出。但是,有些场景下需要保持线程一直处于活动状态,等待新的任务到来,以提高性能或满足特定需求。

如果开发中需要经常在子线程中执行任务,那么频繁的创建和销毁线程就会造成资源的浪费,这不符合我们的初衷。 此时就需要我们对线程进行保活,保证线程在应该处理事情的时候醒来,空闲的时候休眠。

  • runloop 保活

  • GCD 保活,使用 dispatch_semaphore 信号量机制,通过阻塞线程实现保活

缓存NSCache

NSCacheFoundation 框架中提供的一个用于临时存储键值对数据的类,它类似于NSMutableDictionary,但具有自动管理内存和线程安全的特性。NSCache 通常用于缓存临时数据,例如图像、数据对象等,以提高性能和响应速度

  • NSLock 互斥锁
  • atomtic 读写锁(自旋锁)
  • synchronized{} 互斥锁,性能较差
  • dispatch_semaphore 信号量机制
  • NSRecursiveLock 递归锁,锁多次 -> 解多次

计时

  • NSTime 有精度问题,受 runLoop 模式影响
  • CADisplayLink 基于屏幕刷新率的计时器
  • GCD 延时操作,GCD 定时器,使用系统时钟
  • UIView 动画时间
  • NSDate 使用系统时钟

多线程

  • NSOperation,线程依赖、开始、暂停、中止
  • GCD 强调队列和执行,有一次性执行函数、延迟执行函数、栅栏函数、信号量机制函数、Group
  • NSOperationGCD侧重点不同,性能上无谁更优一说

卡顿检测

  • 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 协议
  • 实现 encodeWithCoderinitWithCoder 方法,在内部做手动属性存储

关联对象

本质是对象内存维护的一个哈希表,存储在里面

消息转发

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

image.png

MVC 是一种经典的架构模式,由以下三个组件组成:

  • Model(模型):负责处理数据和业务逻辑。它封装了应用程序的数据和状态,并提供了访问和修改数据的接口。
  • View(视图):负责用户界面的展示和用户交互。它根据模型的数据来渲染用户界面,并将用户的输入事件传递给控制器进行处理。
  • Controller(控制器):负责处理用户输入和控制应用程序的流程。它接收来自视图的用户输入事件,更新模型的数据和状态,然后通知视图进行相应的更新。

MVC 核心思想是将应用程序分为三个组件,每个组件负责不同的职责。这样可以实现数据、逻辑和界面的分离,提高代码的可读性和可维护性。同时,MVC 持多个视图和控制器的组合,以适应不同的用户界面需求。

image.png

MVVM 一种在 MVC 基础上演化而来的架构模式,由以下三个组件组成:

  • Model(模型):与 MVC 中的模型相同,负责处理数据和业务逻辑。
  • View(视图):与 MVC 中的视图相同,负责用户界面的展示和用户交互。
  • ViewModel(视图模型):负责将模型的数据适配为视图可以使用的形式,并包含了视图的状态和操作。它充当了视图和模型之间的桥梁,将模型的数据绑定到视图上,同时将视图的操作反馈到模型上。

MVVM 的核心思想是引入了视图模型,将视图与模型的直接交互解耦,通过数据绑定的方式实现它们之间的关联。这样可以使视图和模型更加独立,提高代码的可测试性和可维护性。MVVM 也广泛应用于现代的前端开发框架,例如AngularVue.js

总的来说,MVCMVVM 都是常见的软件架构模式,用于组织代码和分离关注点。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:灵活,支持多种图像类型,多帧、透明、常见印刷出版
  • WebPgoogle 开发,文件小,质量好,用于传输

计算图片大小

// 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