秋招ios面试 -- 真题篇(二)

1. 介绍一下传值通知和推送通知

传值通知:传值是进程内的组件通信,范围小,延迟低,不依赖网络

常见方式

a. 直接回调/闭包

如:A 页面弹出 B 页面,B 做完事把结果回传给 A。

b. 委托/协议(delegate)

如:一个对象(委托方/被委托者,通常是"子组件/下游")将某些"决定/事件处理"交给另一个对象(代理/委托对象,通常是"上层/外部")来完成。

c. 通知中心

如:一对多广播,模块松耦合,不想建立直接引用。

Swift 复制代码
// 发送
NotificationCenter.default.post(name: .loginSuccess, object: nil, userInfo: ["userId": "123"])
// 监听
NotificationCenter.default.addObserver(forName: .loginSuccess, object: nil, queue: .main) { note in
    print(note.userInfo?["userId"] as? String ?? "")
}

d. KVO/Combine/Reactive

如:对某个状态变化感兴趣的多个订阅者。

对某个状态变化感兴趣的多个订阅者。

推送通知:系统级消息,显示在通知中心,通常由服务端发起。

远程推送:服务端通过 Apple Push Notification service 下发到设备。

本地推送:App 在本机排程,定时或基于触发条件发送。

静默推送:不弹 UI,唤醒 App 在后台拉数据(节制使用,受系统调度)。

2. 介绍一下NSCache & NSDcitionary的区别

|---------------------------------|----------------------------------------|
| NSCache | NSDictionary |
| 内存缓存,自动清理 | 通用键值存储 |
| NSCache 自身可变(类似于可变字典) | NSDictionary 不可变 |
| 多数操作线程安全:存在内部锁和队列 | NSDictionary/NSMutableDictionary 不线程安全 |
| 会在内存压力下自动移除对象 | 不会自动回收,除非你手动清理或对象释放 |
| 键是 AnyObject,但会强引用 | NSDictionary 支持任意符合 Hashable 的对象 |
| 可设置总成本、总数量上限;系统可能随时回收 | 没有成本概念; |
| 可通过 `NSCacheDelegate` 监听对象被移除 | 无 |
| 适用于图片缓存,计算结果缓存,富文本布局缓存 | 主要就是使用作为一个字典容器 |

3. 介绍一下UIView 与 CALayer

UIView:负责交互和事件的视图对象,管理层级、布局、触摸、手势、Auto Layout、响应链等。
CALayer:负责"绘制与合成"的渲染对象,管理位图内容、圆角阴影、变换、动画,性能更靠近硬件。

每个UIView内部都存在一个属性:view.layer(该属性的类型为CALayer)

视图树与层树一一对应

你可以在一个UIView上继续添加layer,做到轻量绘制而无序更多UIView

如果我们只做展示无交互的装饰元素(分割线、阴影、渐变、形状),我们会采用CALayer,因为其相较于UIView更加轻量,"交互在 View、渲染交给 Layer"

视图树:视图树就是由视图(View)按照父子关系组成的一棵树形结构,用来组织界面上的所有可见元素以及它们的层级关系。

视图树的作用如下:

  1. 布局与位置计算:父视图决定子视图的坐标系和布局范围,Auto Layout 也是沿着树自顶向下/自底向上参与计算。

  2. 事件分发:触摸、手势等事件沿着视图树进行命中测试(hit-testing)和响应链传递。

  3. 渲染合成:系统以视图树为蓝本,生成对应的 CALayer 树,在 GPU 进行合成与绘制。

  4. 生命周期与管理:添加/移除、隐藏/显示、层级顺序(z-order)管理都依赖树结构。

4. 介绍一下OC中的category & extension

Category:在不修改原始类源码的情况下,给类添加方法

运行时加载(链接期合并,可以覆盖同名方法,按照编译顺序进行覆盖)

不能直接添加实力变量,但是可以添加属性声明,需要你自己实现getter和setter(或者通过关联对象)

逻辑拆分:把庞大类按功能拆成多个分类文件。
Extension:在"类的实现文件(.m)内部"给类补充"私有的"声明(方法、属性、实例变量)。

仅对当前编译单元可见,避免暴露到外部头文件。

可以隐藏内部细节

关于category的底层原理:

编译后,分类会被编译成一个 `category_t` 结构体并放入镜像(Mach‑O)特定段中,供运行时加载。

cpp 复制代码
// 每个分类一个入口
struct category_t {
    const char *name;           // 类名(C 字符串)
    classref_t cls;             // 指向目标类(运行时填充)
    struct method_list_t *instanceMethods; // 实例方法列表
    struct method_list_t *classMethods;    // 类方法列表(作用在元类)
    struct protocol_list_t *protocols;     // 该分类声明遵循的协议
    struct property_list_t *instanceProperties; // 属性列表(仅声明)
    // 还有一些可选字段:classProperties(新 runtime)
};

当 App 启动或动态库加载时,Objective‑C 运行时会执行"镜像扫描与注册":

  1. dyld 加载镜像(Mach‑O),定位 Objective‑C 的各段(__objc_classlist、__objc_catlist 等)。

  2. runtime 遍历 `__objc_catlist`,拿到每个 `category_t`。

  3. 随后对每个分类进行合并:合并规则通常是"把分类的方法列表插到现有方法列表的前面",使其在方法查找顺序中优先生效。

关联对象:关联对象把"某个对象 A 的一个键"关联到"任意值(对象)",由 runtime 管理一张全局哈希表。

5. CFSocket使用步骤

  1. 准备 CFSocketContext,用于在回调中携带 self/对象指针。

  2. 使用 `CFSocketCreate` 指定协议族、套接字类型、协议、感兴趣事件、回调函数和上下文。

常见参数:

协议族:`PF_INET`(IPv4)、`PF_INET6`(IPv6)

类型:`SOCK_STREAM`(TCP)、`SOCK_DGRAM`(UDP)

协议:`IPPROTO_TCP`、`IPPROTO_UDP`

  1. 配置本机地址或远端地址

  2. 绑定/连接

  3. 将 CFSocket 加入 RunLoop

  4. 在回调中处理事件

  5. 读写数据

  6. 关闭与清理

相关推荐
孓最求完美2 小时前
Charles 进阶抓包教程:突破 SSL Pinning、代理检测与 Flutter 抓包限制
面试
thesky1234563 小时前
智能体面试准备(九):工作流编排——DAG 与状态机两种范式的取舍与手写实现
面试·agent·状态机·工作流·智能体
Revolution613 小时前
reactive 对象重新赋值后,表单为什么没有恢复默认值
前端·vue.js·面试
HeiSenBerg3 小时前
Android Handler 机制完全解析:从源码到内存泄漏,一篇就够了
面试
only-qi5 小时前
大模型Agent面试攻略:落地工程痛点、评估体系与Agentic RAG核心精讲
人工智能·算法·面试·职场和发展·langchain·rag
数字宝藏5 小时前
15本电子书分享
职场和发展·职场发展·教育电商
ShineWinsu6 小时前
对于Linux:传输层协议UDP原理的解析
linux·c++·面试·udp·协议·传输层·计算机系统
GitLqr6 小时前
StatefulWidget 里的隐形炸弹:为什么不要在 State 类中使用 context.mounted
flutter·面试·dart
梦想的颜色7 小时前
2026 AI Agent 工程师完整技术图谱|从面试题「什么是本体 Ontology」切入,附精选面试题库
面试·知识图谱·langgraph·aiagent·大模型面试·本体·2026 面试真题
不简说8 小时前
JS 代码技巧 vol.10 — 20 个 V8 引擎原理,解释"为什么这样写快"
前端·javascript·面试