在超大规模项目中,如何节约AI编程工具Token
一、问题:Token 正在吃掉你的生产力
如果你在一个中小型项目上用 AI 编程工具(Cursor、Copilot、Qoder CLI),体验可能是「真香」。但当你把 AI 丢进一个 3 万+ 源文件、4000+ 行 AppDelegate、双 APP + 三引擎 的超大规模 iOS 项目中,画风就变了:
- 上下文窗口频繁打满,AI 开始「失忆」
- 每轮对话消耗数千 Token,很快就到限额
- AI 给出的建议越来越离谱——因为它根本看不完你的项目
- 修改一个功能,AI 却加载了整个 AppDelegate 和所有无关模块
这不是 AI 不行,是你的项目结构在强制 AI 无效消耗 Token。
本文以某企业 iOS 项目的实际重构经验为例,分享我们在 Token 节约 维度上总结的 7 条实战策略。
二、我们的项目有多「大」
先给个直观的数字:
| 指标 | 数值 |
|---|---|
| 源文件数(.m/.h/.mm) | 31,801 个 |
| 代码行数 | 约 167 万行(仅算 ObjC) |
| AppDelegate.mm | 4,376 行 |
| 主工程目录 | 6,544 个文件 |
| 依赖引擎 | Unity + Cordova + Flutter |
| 模块数 | 30+ Feature Pod |
光是 AppDelegate 就有 4376 行。如果你的 AI 工具每次要加载这个文件来分析一个按钮点击的逻辑——那 90% 的 Token 都浪费在了无关代码上。
三、7 条 Token 节约实战策略
策略 1:模块化拆分——让 AI 只加载它需要的上下文
问题:单工程 + 单 Target 下,AI 无法区分「我该看哪里」。一个来自 主工程/classes/ 的调用可能跳转到另一个目录的代码,AI 不得不把整个项目索引都拉进上下文。
解法:用 CocoaPods 本地 Pod 做物理拆分。
# Podfile
pod 'MedicoolCore', :path => 'MedicoolCore/'
pod 'YikuFeatureBase', :path => 'YikuFeatureBase/'
pod 'YikuAbstract', :path => 'YikuAbstract/'
pod 'FeatureHomeV6', :path => 'Features/FeatureHomeV6/'
每个模块一个独立 Pod,有明确的 podspec 和头文件暴露边界。当 AI 需要分析首页功能时,它的上下文只需要加载:
FeatureHomeV6/— 首页模块代码YikuAbstract/— 抽象层协议YikuFeatureBase/— 基础工具(非全部源码)
而不是:整个 主工程/ + 30 个 Feature 模块 + 三引擎集成代码。
效果:AI 分析一个模块的平均 Token 消耗降低 约 60%(实测从 ~8K 降到 ~3K)。
策略 2:路由解耦——消灭跨模块 Import
问题:传统架构中,FeatureA 调用 FeatureB 需要 #import "FeatureBViewController.h" 并直接 alloc/push。AI 分析这条链路时,必须递归加载 FeatureB 的所有头文件——Token 爆炸。
解法:FeatureRouter + 统一调度层。
// 注册(只在模块启动时执行一次)
[[FeatureRouter sharedRouter] registerRoute:@"feat.homev6.search"
handler:^(UIViewController *src, NSDictionary *params) {
FHV6SearchVC *vc = [[FHV6SearchVC alloc] init];
[src.navigationController pushViewController:vc animated:YES];
}];
// 调用(一行代码,无 Import)
[YikuFeatureDispatcher startSearchWithSourceVC:self params:nil];
关键设计:
FeatureRouter是 O(1) 注册/查询的单例路由表YikuFeatureDispatcher对外暴露 150+ 静态方法,只增不减- 调用方不需要 import 目标模块的任何头文件
Token 收益:AI 分析调用链时,不需要递归加载目标模块的源码。它只需要知道 Dispatcher 方法的签名和路由 key——上下文从 5 层递归降到 1 层。
策略 3:Entry 注册模式——给 AI 一个「文件索引」
问题:AI 在超大规模项目中最大的痛点是 不知道从哪里入手。它可能在 3 万个文件中迷失。
解法:每个模块实现一个 XxxEntry 类,统一注册路由。
// FeatureHomeV6/Classes/HomeV6Entry.m
@implementation HomeV6Entry
+ (void)registerFeatureRouter {
FeatureRouter *router = [FeatureRouter sharedRouter];
[router registerRoute:@"feat.homev6.search" handler:^...{}];
[router registerRoute:@"feat.homev6.discovery" handler:^...{}];
[router registerRoute:@"feat.homev6.me" handler:^...{}];
}
@end
然后在 AppDelegate 中统一调用:
- (void)initFeatureRouter {
[HomeV6Entry registerFeatureRouter];
[LiveChatRoomEntry registerFeatureRouter];
[ShareEntry registerFeatureRouter];
// ... 28 个 Entry
}
Token 收益:AI 理解一个功能的入口点变成了 读一个 Entry 文件(~50 行),而不是在 4000 行的 AppDelegate 中搜索。
策略 4:资源隔离——别让 AI 分析重复的资源
问题:使用 s.resources 且用 **/* 通配符时,CocoaPods 会把资源扁平化复制到 main bundle。AI 在分析资源引用时,经常搞不清「这个图片到底属于哪个模块」,因为所有资源都在同一个命名空间里打架。
解法:使用 resource_bundles + 单级目录 规则。
# ❌ 旧写法 - 资源重复打包,AI 无法区分
s.resources = 'FeatureHomeV6/Assets/**/*'
# ✅ 新写法 - 资源隔离,AI 一目了然
s.resource_bundles = {
'FeatureHomeV6' => [
'FeatureHomeV6/Assets/Media/*.xcassets',
'FeatureHomeV6/Assets/Media/*.lproj',
'FeatureHomeV6/Assets/Controllers/*.xib',
'FeatureHomeV6/Assets/Cell/*.xib'
]
}
配合模块内的 ResourceBundle 类:
// FHV6ResourceBundle.m
+ (NSBundle *)featureBundle {
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
NSURL *bundleURL = [bundle URLForResource:@"FeatureHomeV6" withExtension:@"bundle"];
return bundleURL ? [NSBundle bundleWithURL:bundleURL] : bundle;
}
Token 收益:AI 分析资源引用时,可以精确追溯到模块内部的 bundle,不需要在全局资源池中搜索。
策略 5:FeatureDefines.h——用常量替代硬编码
问题:indexPath.row == 3 这样的硬编码在大型项目中数不胜数。AI 分析一个 tableView 方法时,不得不加载整个数据源的上下文来判断 row == 3 是哪个功能。
解法:定义全局功能键常量,彻底消灭 magic number。
// Features/FeatureDefines.h
#define FEATURE_ACTION_HOME_DISCOVERY @"home_discovery"
#define FEATURE_ACTION_HOME_LIVE @"home_live"
#define FEATURE_ACTION_HOME_VIDEO @"home_video"
Token 收益:AI 看到 FEATURE_ACTION_HOME_LIVE 就知道这是直播入口,不需要分析周围的数值上下文。每个判断节省 ~200 Token 的上下文解释。
策略 6:小文件原则——一口吃得下的上下文
问题:4376 行的 AppDelegate 是 Token 黑洞。AI 加载它一次就占了 ~10K Token,而其中 90% 的内容与当前任务无关。
解法:把巨型文件按职责拆分。
重构前的 AppDelegate(4376 行):
- 应用生命周期:200 行
- 第三方 SDK 初始化:800 行
- 路由注册:100 行
- 推送回调:600 行
- 自动登录:400 行
- ... 其余 2000+ 行杂项
重构方向:每个职责独立为文件,AppDelegate 只做编排。
Token 收益:AI 加载一个 ~100 行的 RouterRegistration.swift 比加载 4376 行的 AppDelegate 节省 97% 的 Token。
策略 7:明确的依赖方向——防止 AI「绕路」
问题:循环依赖或混乱的依赖关系会让 AI 陷入死循环分析。它会反复加载 A→B→C→A 的代码,浪费大量 Token 却得不到有效结论。
解法:建立单向依赖链,用 podspec 的 s.dependency 强制执行。
依赖方向(从上到下,禁止反向):
Features/* → YikuAbstract → YikuFeatureBase → MedicoolCore
→ MedicoolNetwork
→ MedicoolComponent
Token 收益:AI 分析 Feature 模块时,只需要沿依赖方向向上查找,永远不会走回头路。上下文加载路径从 O(n²) 降到 O(n)。
四、实战数据:重构前后的 Token 对比
以一个典型的「AppDelegate 初始化 → 点击首页搜索按钮 → 跳转搜索页面」场景为例:
| 维度 | 重构前 | 重构后 | 节约比例 |
|---|---|---|---|
| AI 需加载的源文件 | ~50 个(整个 AppDelegate + 所有 Entry + 目标 VC 链) | ~8 个(Router + Entry + Dispatcher + VC) | 84% |
| 上下文内容量 | ~25,000 Token | ~3,500 Token | 86% |
| AI 理解调用链需要深度 | 5 层递归 Import | 1 层路由查找 | 80% |
| 误判率(无效建议占比) | ~40% | ~10% | 75% |
数据来源于我们内部使用 AI 工具重构该项目的实测对比。
五、总结:Token 节约的本质是信息密度
AI 编程工具的 Token 消耗,和项目结构的信息密度直接相关。你的代码写得越清晰、模块边界越干净、依赖关系越简单,AI 就能在有限的 Token 预算内做出越准确的判断。
不要把 Token 浪费在让 AI 理解「这个 indexPath.row 是什么功能」「这个 import 链最终会走到哪里」「这个资源属于哪个模块」——这些都是人应该替 AI 解决好的问题。
好的架构,就是对 AI 也友好的架构。
附录:我们的重构工具链
| 用途 | 技能/工具 |
|---|---|
| 功能解耦 → 路由化 | .skill/feature-router |
| 资源隔离 → resource_bundles | .skill/single-level-resource-bundle |
| 更新资源加载模式 | .skill/update_submodule_with_resource_bundle |
| 第三方头文件修复 | .skill/fix_third_import |
| 代码评审与质量检查 | Skill: simplify |
| 安全审查 | Skill: security-review |
本文基于某企业 iOS 项目的真实重构经验编写。