秋招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. 关闭与清理

相关推荐
Aspect of twilight1 小时前
2025华为AI岗实习面试深度学习基础知识
人工智能·深度学习·面试
EB_Coder2 小时前
前端面试题-JavaScript中级篇
前端·javascript·面试
量子炒饭大师2 小时前
David自习刷题室——【蓝桥杯刷题备战】乘法表
c语言·c++·git·职场和发展·蓝桥杯·github·visual studio
牛客企业服务2 小时前
2025年AI面试趋势解析:企业如何借力工具破解规模化招聘难题?
人工智能·面试·职场和发展
公众号/头条号:技术很有趣2 小时前
2025年11月 系统架构设计师考试复盘
职场和发展·软件工程
资深web全栈开发3 小时前
LeetCode 3623. 统计梯形的数目 I
算法·leetcode·职场和发展·组合数学
自动化测试薰儿13 小时前
软件测试经典面试题整理(一)
软件测试·职场和发展
不会编程的小寒14 小时前
数据库编程 面试
数据库·面试·职场和发展
Wenhao.16 小时前
LeetCode Hot100 腐烂的橘子
算法·leetcode·职场和发展