iOS 面试题

104 阅读7分钟

1. 什么是逃逸闭包?

当闭包作为一个实际参数传递给一个函数或者变量的时候,我们就说这个闭包逃逸了,可以在形式参数前写 @escaping 来明确闭包是允许逃逸的。

  • 非逃逸闭包、逃逸闭包,一般都是当做参数传递给函数
  • 非逃逸闭包:闭包调用发生在函数结束前,闭包调用在函数作用域内
  • 逃逸闭包:闭包有可能在函数结束后调用,闭包调用逃离了函数的作用域,需要通过@escaping声明

2. 沙盒目录结构是怎样的?

沙盒结构

  • Documents:常用目录,iCloud备份目录,存放数据,这里不能存缓存文件,否则上架不被通过

  • Library

    • Caches:存放体积大又不需要备份的数据,SDWebImage缓存路径就是这个
    • Preference:设置目录,iCloud会备份设置信息
  • tmp:存放临时文件,不会被备份,而且这个文件下的数据有可能随时被清除的可能

3.说一下 http中的 get 和 post 区别?

  • get 一般用于从服务端获取数据,post 用于向服务端发送数据
  • get 参数拼接在 url 地址里面, post 参数则放在其包体里,post 比 get 稍安全,隐秘
  • get 可以被缓存,可以存储在浏览器浏览历史中

4.HTTPS连接过程简述

  1. 客户端向服务端发起 https 请求
  2. 服务器(需要申请 ca 证书),返回证书(包含公钥)给客户端
  3. 客户端使用根证书验证 服务器证书的有效性,进行身份确认
  4. 客户端生成对称密钥,通过公钥进行密码,发送给服务器
  5. 服务器使用私钥进行 解密,获取对称密钥
  6. 双发使用对称加密的数据进行通信

5.weak 和 assign 的不同点

  • weak 策略在属性所指的对象遭到摧毁时,系统会将 weak 修饰的属性对象的指针指 向 nil,在 OCnil 发消息是不会有什么问题的;
  • 如果使用 assign 策略在属性所指 的对象遭到摧毁时,属性对象指针还指向原来的对象,由于对象已经被销毁,这时候就产生了野指针,如果这时候在给此对象发送消息,很容易造成程序崩溃。
  • assigin 可以用于修饰非 OC 对象,而 weak 必须用于 OC 对象

6. 遇到过BAD_ACCESS的错误吗?

BAD_ACCESS 报错属于内存访问错误,会导致程序崩溃, 错误的原因是访问了野指针(悬挂指针)。

7. 说一下iOS 中的APNS,远程推送原理?

Apple push Notification Service,简称 APNS,是苹果的远程消息推送,原理如下:

  1. iOS 系统向APNS服务器请求手机端的deviceToken
  2. App 接收到手机端的 deviceToken,然后传给 App 对应的服务器.
  3. App 服务端需要发送推送消息时, 需要先通过 APNS 服务器
  4. 然后根据对应的 deviceToken 发送给对应的手机

8. 在iOS开发中,UIView的frame和bounds在以下哪种情况下是不相等的?

当视图进行了旋转或缩放变换时

9, iOS 开发 内存泄漏常见场景及检测方案

  1. 核心本质:内存泄漏是对象引用计数无法归 0。 核心原因是“循环引用”和“未及时释放强引用”;

  2. 必记场景:Block 循环引用、NSTimer 泄漏、代理 strong 修饰、通知/KVO 未移除,

各场景的解决方案必须熟练;

10,iOS多线程之GCD

同步任务嵌套同步任务,会引发阻塞,原因在于,第一个任务执行完毕的条件是第二个任务执行完毕,而第二个任务执行完毕的条件是第一个任务执行完毕,因此二者相互等待,无法结束。

11, 造成死锁的四个条件:

  1. 互斥条件:某个资源一次只能被一个线程占用。
  2. 占有且等待:一个线程占有一个资源,同时等待其他资源。
  3. 不可剥夺:线程所持有的资源不能被强制剥夺。
  4. 循环等待:两个或多个线程形成一种循环等待关系。

12,前端请求

前端请求表面看只是“调接口拿数据”,但真正完整的过程,远不止这一句。它涉及用户操作、事件触发、浏览器网络通信、服务器处理、数据返回、前端解析和页面渲染,是一整条完整链路。

13,进程和线程

进程和线程的核心区别在于:‌

进程是资源分配的基本单位,而线程是 CPU 调度的基本单位‌。简单来说,进程像一个独立的工厂,拥有自己的场地和资源;线程则是工厂里的工人,共享工厂资源但独立执行任务。‌‌ ‌

资源与内存分配

‌进程‌:拥有独立的地址空间和系统资源(如内存、文件句柄),进程间相互隔离,一个进程崩溃通常不会影响其他进程 。 ‌线程‌:不拥有系统资源,仅拥有少量运行必需资源(如栈、寄存器),共享所属进程的全部资源(如内存、打开的文件)。

14,Source0 和‌ Source1

Source0和‌Source1‌是 iOS/macOS 中 RunLoop 的两种输入源(CFRunLoopSourceRef),核心区别在于‌触发机制‌与‌唤醒能力‌:Source0 处理应用层手动触发事件(‌不能主动唤醒‌休眠线程),Source1 处理基于 Mach Port 的系统/内核事件(‌能主动唤醒‌休眠线程)。‌‌**

核心定义与功能对比
  • Source0(非基于 Port 的事件源)

    • 本质‌:仅包含回调函数指针(version 0 context),无端口机制。
    • 职责‌:处理 App 内部生成的事件,如 performSelector:afterDelay:、UI 触摸事件的上层分发、自定义业务逻辑触发。
    • 唤醒特性‌:‌无法主动唤醒‌休眠的 RunLoop;需先调用 CFRunLoopSourceSignal 标记待处理,再手动调用 CFRunLoopWakeUp才能执行回调。
    • 典型场景‌:按钮点击响应、手势识别后的业务逻辑、线程间同进程的任务调度。‌‌**
  • Source1(基于 Mach Port 的事件源)

    • 本质‌:包含 Mach Port 端口号和回调函数(version 1 context),由内核管理。
    • 职责‌:接收系统内核、其他进程或线程通过端口发送的消息,如硬件事件底层捕获、跨进程通信(IPC)、网络数据到达通知。
    • 唤醒特性‌:‌能主动唤醒‌休眠的 RunLoop;当端口有消息时,内核直接中断线程使其立即处理回调。
    • 典型场景‌:屏幕触摸底层信号接收、dispatch_async 主队列任务、系统通知、Socket 底层数据读取。‌‌
工作流程关键差异
  1. 触发流程‌:Source0 需“标记 + 手动唤醒”两步;Source1 仅需“端口发消息”一步,内核自动触发。
  2. 事件流向‌:硬件事件通常先由 Source1 捕获(内核态),转化为应用层事件后交由 Source0 处理(用户态)。
  3. 生命周期‌:Source0 单次触发后常被移除;Source1 端口可持续监听,支持重复触发。‌‌**

简言之,Source0 负责“应用想做的事”,Source1 负责“系统通知你做的事”。两者协同确保 RunLoop 在休眠时节能、有事时即时响应。

15,避免大图 OOM 的黄金法则:‌ “磁盘下载、子线程解码、按需缩放、分块渲染” ‌。

务必记住:‌图片文件大小(File Size)不等于内存占用(Memory Footprint) ‌。内存占用取决于‌像素总数 × 每像素字节数‌。通过 ImageIO 控制解码尺寸,是从根源上解决大图内存问题的最佳方案。

1. 基础下载:异步写入文件(避免内存爆炸)

不要使用 NSData *data = [NSData dataWithContentsOfURL:url],这会将几 MB 甚至几十 MB 的数据一次性载入内存。

‌推荐做法:使用 NSURLSessionDownloadTask

  • 优势‌:系统直接以流式方式写入磁盘临时文件,内存占用极低。

  • 代码逻辑‌:

    • 创建 NSURLSessionDownloadTask
    • 在回调 didFinishDownloadingToURL: 中获取临时文件路径。
    • 将临时文件移动(moveItemAtPath)到你的缓存目录(如 Library/Caches)。
  1. 高效解码与缩放:ImageIO 框架(避免主线程卡顿)

直接从文件读取 UIImage (imageWithContentsOfFile) 会在主线程进行解码,且默认加载全分辨率像素。对于大图,必须‌在子线程解码‌并‌按需缩放‌。

推荐做法:使用 CGImageSource‌

优势‌:支持指定最大像素尺寸解码,避免将 4000x3000 的图片解码进内存,只解码屏幕需要的 400x300。

关键参数‌:kCGImageSourceThumbnailMaxPixelSize。

16,HTTP 304状态码的详细讲解