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)按照父子关系组成的一棵树形结构,用来组织界面上的所有可见元素以及它们的层级关系。
视图树的作用如下:
布局与位置计算:父视图决定子视图的坐标系和布局范围,Auto Layout 也是沿着树自顶向下/自底向上参与计算。
事件分发:触摸、手势等事件沿着视图树进行命中测试(hit-testing)和响应链传递。
渲染合成:系统以视图树为蓝本,生成对应的 CALayer 树,在 GPU 进行合成与绘制。
生命周期与管理:添加/移除、隐藏/显示、层级顺序(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 运行时会执行"镜像扫描与注册":
-
dyld 加载镜像(Mach‑O),定位 Objective‑C 的各段(__objc_classlist、__objc_catlist 等)。
-
runtime 遍历 `__objc_catlist`,拿到每个 `category_t`。
-
随后对每个分类进行合并:合并规则通常是"把分类的方法列表插到现有方法列表的前面",使其在方法查找顺序中优先生效。
关联对象:关联对象把"某个对象 A 的一个键"关联到"任意值(对象)",由 runtime 管理一张全局哈希表。
5. CFSocket使用步骤
-
准备 CFSocketContext,用于在回调中携带 self/对象指针。
-
使用 `CFSocketCreate` 指定协议族、套接字类型、协议、感兴趣事件、回调函数和上下文。
常见参数:
协议族:`PF_INET`(IPv4)、`PF_INET6`(IPv6)
类型:`SOCK_STREAM`(TCP)、`SOCK_DGRAM`(UDP)
协议:`IPPROTO_TCP`、`IPPROTO_UDP`
-
配置本机地址或远端地址
-
绑定/连接
-
将 CFSocket 加入 RunLoop
-
在回调中处理事件
-
读写数据
-
关闭与清理