路由的讲解
传统路由面临的痛点是: URL Router 虽然灵活但类型不安全(参数容易传错),而 Protocol Router 虽然安全但耦合度高且灵活性差。
所以LJRouter这套框架的核心思路是:用‘编译期注入’解决注册分散的问题,用‘运行时 NSInvocation’解决动态调用性能和类型转换的问题。 它本质上是一个结合了静态检查和动态调用的高性能分发中心。”
第二阶段:核心技术亮点(层层递进)
1. 注册机制:由“手动维护”进化为“Mach-O 注入”
“首先是注册机制。为了避免在
+load方法里写大量的注册代码拖慢启动速度,也为了避免手动维护一个巨大的注册表,我采用了去中心化的方案。利用 Clang 的 section 属性,在编译期把路由的元数据(Key、类名、方法签名)直接写进 Mach-O 的
__DATA段。这样在 App 启动或者首次使用时,直接通过
getsectiondata从二进制里读取这些结构体。这样做的好处是主工程零感知,业务模块只要写一个宏就能自动注册,而且读取二进制段的速度比执行几千次registerURL方法要快得多。”
2. 调用机制:NSInvocation 与 智能匹配
“其次是调用层。市面上的 Router 大多只能传字符串,但我们希望能像原生调用一样传 Image、Model 甚至结构体。
所以LJRouter没有用简单的 Block 回调,而是基于 NSInvocation 构建了一套调用中心。
这里有一个难点是类型匹配。比如 URL 传进来是字符串 “18”,但目标方法要
int。我们做了一个自动类型转换层,还能处理剩余参数。更重要的是,为了解决同一个 Key 对应不同参数(类似重载)的问题,我实现了一套评分匹配算法。路由会根据传入参数的字段覆盖率,自动去匹配最合适的那个 Target 方法。这让路由调用具备了类似 C++ 的 Overload 能力。”
3. 架构治理:双中心与拦截器 AOP
(体现你的架构分层意识)
话术:
“最后是架构分层。为了职责单一,我把路由拆分为 Page(页面跳转) 和 Action(功能调用) 两个中心。
并在外层包裹了一套基于 AOP 的拦截器机制。不同于简单的 Block 拦截,我设计了一个包含 Before/After 节点和优先级队列的拦截系统,可以非常优雅地处理像‘登录检查’、‘埋点监控’这些横切关注点。
同时,所有的路由节点都会生成监控事件,配合我们的 APM 平台,可以实时监控路由的死链和性能耗时。”
第三阶段:防守与追问(面试官可能会问的问题)
面试官听完上面的描述,通常会觉得“很屌”,但会试图挑战你。你要准备好以下回答:
Q1: 使用 NSInvocation 会不会有性能问题?
- 回答:相比于直接的方法调用(objc_msgSend),NSInvocation 确实有微小的开销,但相比于 URL 解析和字符串匹配的耗时,这层开销是可以忽略不计的。而且我们做了缓存,对于高频路由,Method Signature 是缓存的。
Q2: 为什么不直接用 Protocol(接口绑定)的方式?
- 回答:Protocol 方式需要业务模块之间依赖公共的 Protocol 头文件,这在一定程度上还是存在依赖。而 LJRouter 通过字符串 Key 和参数字典解耦,做到了完全的编译期隔离。同时我们保留了编译期检查机制(通过宏记录签名),在开发阶段就能暴露出参数类型不匹配的问题,兼顾了 Protocol 的安全性。
Q3: 遇到组件化拆分时,不同模块的依赖怎么解?
- 回答:这正是 LJRouter 的强项。我们支持上下文自动注入(SenderContext)。路由内部会利用 Responder Chain 自动找到当前的 VC、Window 等环境参数注入给目标模块,目标模块不需要关心是谁调用的它,彻底解耦。
总结:你的“人设”关键词
在表述时,时刻通过眼神和语气强调以下关键词,加深面试官印象:
-
侵入性低(Macro, Section 注入)
-
类型安全(编译期检查,运行时转换)
-
原生体验(NSInvocation,传对象)
-
可观测性(KeyNode 监控)
最后的话术(Next Step):
“这套框架上线后,支撑了我们 App 数百个业务模块的解耦,Crash 率也因为类型检查机制降低了不少。如果您对其中的 Mach-O 注入细节或者类型转换逻辑感兴趣,我可以展开讲讲。”
这样讲,你不是在背书,而是在复盘你的技术决策,这才是架构师该有的样子。