iOS开发基础82-iOS项目目录结构:从 MVC 到组件化的工程实践
iOS 项目目录结构完全指南:从 MVC 到组件化的工程实践
一、为什么目录结构是面试高频考点
一句话原理
目录结构是项目的"骨架",面试官问目录结构,本质是在考察你的工程经验、架构思维和团队协作能力——一个有经验的开发者,能通过清晰的目录结构让新人快速定位代码,让团队协作效率最大化。
目录结构的三个核心价值
| 价值 | 说明 |
|---|---|
| 快速定位 | 看到一个功能,知道去哪个目录找代码,不用全局搜索 |
| 职责清晰 | 每个目录有明确的职责边界,避免代码乱放、重复造轮子 |
| 团队协作 | 统一的目录规范让多人协作时不会互相覆盖、不会找不到文件 |
面试时如果被问"你们项目的目录结构是怎样的",不要只说"按 MVC 分的",要讲清楚分层逻辑、每个目录的职责、为什么这么分、遇到了什么问题,这样才能体现经验。
二、两种主流目录结构
结构一:主目录按业务分类,内目录按模块(MVC)分类
一句话原理
先按业务模块分大目录(如发现、关注、我的),每个业务目录内部再按 MVC 分层(Controller、View、Model)。
目录结构示意
MyApp/
├── Main/ # 主要业务
│ ├── Discover/ # 发现业务模块
│ │ ├── Controller/ # 控制器
│ │ ├── View/ # 视图
│ │ ├── Model/ # 模型
│ │ └── Other/ # 该模块的其他文件(如 ViewModel、工具)
│ ├── Follow/ # 关注业务模块
│ │ ├── Controller/
│ │ ├── View/
│ │ ├── Model/
│ │ └── Other/
│ ├── FriendCircle/ # 简友圈业务模块
│ │ ├── Controller/
│ │ ├── View/
│ │ ├── Model/
│ │ └── Other/
│ ├── Me/ # 我的业务模块
│ │ ├── Controller/
│ │ ├── View/
│ │ ├── Model/
│ │ └── Other/
│ └── Other/ # 其他公共业务
│ ├── TabBar/ # 自定义 TabBar
│ ├── Navigation/ # 导航控制器
│ ├── Public/ # 公用控制器
│ ├── Login/ # 登录注册
│ ├── Setting/ # 设置
│ ├── Release/ # 发布器
│ └── Other/
├── Expand/ # 拓展层(基础能力)
│ ├── DataBase/ # 数据库
│ ├── Category/ # 分类
│ ├── Network/ # 网络层
│ ├── Tool/ # 工具类
│ ├── Macros/ # 宏定义
│ └── Const/ # 常量
├── Vender/ # 第三方库(手动导入的)
│ ├── AFNetworking/
│ └── SDWebImage/
└── Resource/ # 资源文件
├── Plist/
└── Image/
优点
- 业务聚合度高:一个业务模块的所有代码都在同一个目录下,修改"发现"模块时只需要关注
Discover/目录。 - 符合模块化思维:每个业务模块相对独立,便于后续拆分为独立组件。
- 新人上手快:按业务找代码,符合人的直觉思维。
缺点
- MVC 层分散:所有 Controller 分散在各个业务目录中,如果想统一修改所有控制器的基类,需要来回切换。
- 公共类归属模糊:多个业务模块共用的类(如通用的 TableViewCell)不知道该放哪个业务目录。
- 容易出现重复代码:每个业务模块都有自己的 Controller/View/Model,可能会重复实现相似的功能。
适用场景
- 业务模块划分清晰、模块间耦合度低的项目。
- 中大型项目,团队按业务模块分工(每个人负责一个业务线)。
- 计划未来做组件化拆分的项目。
结构二:主目录按模块(MVC)分类,内目录按业务分类
一句话原理
先按架构分层分大目录(Controller、View、Model),每个层目录内部再按业务模块细分。
目录结构示意
MyApp/
├── Controller/ # 所有控制器
│ ├── Discover/ # 发现模块的控制器
│ ├── Follow/ # 关注模块的控制器
│ ├── FriendCircle/ # 简友圈模块的控制器
│ ├── Me/ # 我的模块的控制器
│ ├── Login/ # 登录注册
│ ├── Setting/ # 设置
│ └── Public/ # 公用控制器
├── View/ # 所有视图
│ ├── Discover/
│ ├── Follow/
│ ├── FriendCircle/
│ ├── Me/
│ ├── Public/ # 公用视图(如通用 Cell)
│ └── Other/
├── Model/ # 所有模型
│ ├── Discover/
│ ├── Follow/
│ ├── FriendCircle/
│ ├── Me/
│ └── Public/
├── ViewModel/ # MVVM 架构下的 ViewModel
│ ├── Discover/
│ └── ...
├── Expand/ # 拓展层(同结构一)
│ ├── Network/
│ ├── Tool/
│ ├── Category/
│ ├── DataBase/
│ ├── Macros/
│ └── Const/
├── Vender/ # 第三方库
└── Resource/ # 资源
优点
- 架构层集中:所有 Controller 在一起、所有 View 在一起,便于统一管理基类、统一修改。
- 公共类归属清晰:公用的 Cell、公用的 Model 直接放在
View/Public/、Model/Public/下。 - 符合 MVC/MVVM 的分层思维:架构分层清晰,适合严格按照架构模式开发的团队。
缺点
- 业务分散:一个业务模块的代码分散在 Controller、View、Model 三个目录中,开发一个功能需要在三个目录间来回切换。
- 文件查找困难:项目大了之后,Controller 目录下有几十个业务子目录,找一个特定的控制器需要层层展开。
- 不利于模块化拆分:业务代码分散,后续拆组件时需要从各个层中抽取代码,成本高。
适用场景
- 小型项目,业务模块少,分层简单。
- 团队按架构角色分工(有人专门写 View、有人专门写 Model)。
- 严格遵循 MVC/MVVM 架构,强调分层规范的项目。
两种结构对比总结
| 对比维度 | 结构一(业务为主) | 结构二(分层为主) |
|---|---|---|
| 一级目录 | 业务模块 | 架构层(Controller/View/Model) |
| 二级目录 | MVC 分层 | 业务模块 |
| 业务聚合度 | 高(一个业务的代码在一起) | 低(分散在各层) |
| 公共类归属 | 模糊(不知道放哪个业务) | 清晰(放对应层的 Public 下) |
| 模块化拆分 | 容易(直接拆业务目录) | 困难(需要从各层抽取) |
| 新人上手 | 快(按业务找) | 慢(需要理解分层) |
| 适用项目规模 | 中大型 | 小型 |
| 团队分工方式 | 按业务线分工 | 按架构角色分工 |
核心建议:中大型项目优先选结构一(业务为主),因为业务聚合度高、便于模块化拆分、团队按业务线分工更高效。小型项目可以选结构二,分层简单清晰。
三、标准目录各层详解
无论哪种结构,通常都会有以下几个核心层级,下面逐一解析每个目录的职责和命名规范。
1. Main / Classes(业务层)
职责:存放所有业务相关的代码,是项目的核心目录。
命名规范:
- 业务模块名用英文,首字母大写,如
Discover、Follow、FriendCircle。 - 可以加中文注释(如
Discover(发现)),方便不熟悉业务的人理解,但大型项目或开源项目建议纯英文。 - 每个业务模块内部按 MVC/MVVM 分层:
Controller、View、Model、ViewModel、Other。
子目录说明:
| 子目录 | 职责 | 命名示例 |
|---|---|---|
Controller |
该业务模块的所有控制器 | DiscoverViewController.h、DiscoverDetailViewController.h |
View |
该业务模块的自定义视图 | DiscoverCell.h、DiscoverHeaderView.h |
Model |
该业务模块的数据模型 | DiscoverModel.h、DiscoverItem.h |
ViewModel |
MVVM 架构下的视图模型 | DiscoverViewModel.h |
Other |
该模块特有的工具、管理器等 | DiscoverDataManager.h |
2. Expand / Common / Base(拓展层 / 基础层)
职责:存放跨业务模块共用的基础能力,是项目的"基础设施"。
常见子目录:
| 子目录 | 职责 | 内容示例 |
|---|---|---|
Network |
网络层封装 | HttpRequestManager、ApiService、NetworkMonitor |
Tool |
工具类 | DateTool、StringTool、ImageTool、SandboxTool |
Category |
系统类分类 | UIView+Frame、NSString+Hash、UIImage+Resize |
DataBase |
数据库封装 | DataBaseManager、CoreDataStack、RealmManager |
Macros |
宏定义 | AppMacro.h(屏幕宽高、颜色)、NotificationMacro.h |
Const |
常量定义 | AppConst.h(通知名、缓存 key)、URLConst.h(接口地址) |
Base |
基类 | BaseViewController、BaseView、BaseModel、BaseCell |
Router |
路由层(组件化项目) | AppRouter、RouterProtocol |
命名规范:
- 工具类以
Tool结尾,如DateTool。 - 管理器以
Manager结尾,如HttpRequestManager。 - 分类采用
类名+功能格式,如UIView+Frame。 - 宏定义全部大写,如
SCREEN_WIDTH。 - 常量以
k开头,如kUserDidLoginNotification。
3. Vender / Vendor(第三方库)
职责:存放手动导入的第三方库源码。
注意:
- 如果用 CocoaPods / Carthage / Swift Package Manager 管理第三方库,这个目录可以不存在(依赖在
Pods/目录或系统管理)。 - 手动导入的第三方库建议保留原目录结构,不要修改源码,便于后续升级。
- 对第三方库做了修改的,建议单独建
Vender+Custom/目录存放修改后的版本,并注明修改内容。
4. Resource / Assets(资源层)
职责:存放图片、音频、视频、Plist、JSON、字体等资源文件。
常见组织方式:
Resource/
├── Assets.xcassets/ # 图片资源(推荐用 Asset Catalog)
│ ├── AppIcon.appiconset/
│ ├── LaunchImage.imageset/
│ ├── TabBar/ # TabBar 图标
│ ├── Discover/ # 发现模块图片
│ ├── Common/ # 公用图片
│ └── Placeholder/ # 占位图
├── Plist/ # Plist 配置文件
│ ├── CityList.plist
│ └── EnvironmentConfig.plist
├── JSON/ # 本地 JSON 数据(如测试数据、配置)
├── Audio/ # 音频文件
├── Video/ # 视频文件
├── Font/ # 自定义字体
└── HTML/ # 本地 H5 页面
最佳实践:
- 图片资源优先用
Assets.xcassets管理,支持 @2x/@3x、深色模式、矢量图等。 - 图片按业务模块分子目录,避免所有图片堆在一个目录下。
- 公用图片放
Common/,业务图片放对应业务目录。 - 资源文件名加业务前缀,如
discover_avatar_placeholder.png,避免命名冲突。
5. Other / Supporting Files(其他)
职责:存放 AppDelegate、main.m、Info.plist 等入口文件和配置文件。
Other/
├── AppDelegate.h
├── AppDelegate.m
├── main.m
├── Info.plist
└── .pch文件(可选,现在不推荐用)
现在 Xcode 新建项目默认把 AppDelegate 和 Info.plist 放在根目录,不需要单独建
Other/目录,但如果项目文件多,建一个App/或Supporting/目录统一管理入口文件会更清晰。
四、不同架构下的目录结构
目录结构和项目采用的架构模式紧密相关,下面是常见架构的目录特点。
1. MVC 架构
MyApp/
├── Controller/ # 所有控制器
├── View/ # 所有视图
├── Model/ # 所有模型
└── Expand/ # 基础层
特点:严格按 Model-View-Controller 三层分,结构简单。但 Controller 容易臃肿(Massive View Controller),中大型项目不推荐纯 MVC。
2. MVVM 架构
MyApp/
├── Main/
│ ├── Discover/
│ │ ├── Controller/ # 控制器(负责 UI 逻辑、绑定 ViewModel)
│ │ ├── View/ # 视图
│ │ ├── ViewModel/ # 视图模型(负责业务逻辑、数据处理)
│ │ └── Model/ # 数据模型
│ └── ...
└── Expand/
特点:在 MVC 基础上增加了 ViewModel 层,把业务逻辑从 Controller 中抽离出来,Controller 变轻。目录中每个业务模块下都有 ViewModel/ 子目录。
3. VIPER 架构
MyApp/
├── Main/
│ ├── Discover/
│ │ ├── View/ # 视图(被动显示,通知 Presenter)
│ │ ├── Presenter/ # 展示层(处理 UI 逻辑,调用 Interactor)
│ │ ├── Interactor/ # 交互层(业务逻辑、数据获取)
│ │ ├── Entity/ # 实体(数据模型)
│ │ └── Router/ # 路由(页面跳转)
│ └── ...
└── Expand/
特点:分层最细,每个业务模块下有 5 层(View-Presenter-Interactor-Entity-Router),职责最清晰,但代码量最大、开发成本最高。适合大型团队、长期维护的项目。
4. 组件化 / 模块化架构
中大型项目发展到一定阶段,会做组件化拆分,目录结构会发生本质变化:
MyApp/ # 主工程(壳工程)
├── App/ # 主 App 入口
│ ├── AppDelegate
│ ├── MainTabBar/ # 主 TabBar 组装
│ └── AppAssembly/ # 组件注册与组装
├── Modules/ # 业务组件(每个都是独立的 Pod / 库)
│ ├── DiscoverModule/ # 发现组件
│ │ ├── Classes/
│ │ │ ├── Controller/
│ │ │ ├── View/
│ │ │ ├── ViewModel/
│ │ │ └── Model/
│ │ ├── Assets/
│ │ └── DiscoverModule.podspec
│ ├── FollowModule/ # 关注组件
│ ├── MeModule/ # 我的组件
│ ├── LoginModule/ # 登录组件
│ └── ...
├── BasicModules/ # 基础组件(通用能力)
│ ├── NetworkModule/ # 网络组件
│ ├── RouterModule/ # 路由组件
│ ├── ImageModule/ # 图片加载组件
│ ├── DatabaseModule/ # 数据库组件
│ ├── UIKitModule/ # 基础 UI 组件
│ └── ...
└── Podfile # 依赖所有组件
组件化的核心特点:
- 每个业务模块是独立的 Pod 库,有自己的目录结构、资源、依赖。
- 主工程是壳工程,只负责组装各个组件,不包含业务代码。
- 组件间通过路由(Router)或协议(Protocol)通信,不直接依赖。
- 基础组件(网络、图片、数据库等)被所有业务组件依赖。
- 每个组件可以独立编译、独立测试、独立发版。
组件化是中大型项目的必然趋势,但实施成本高,需要团队有一定的架构能力。小项目不要盲目组件化,先把目录结构规范好、模块边界划清,再逐步拆分。
五、目录结构的演进路径
一个项目从初创到成熟,目录结构通常会经历以下演进:
阶段一:初创期(0~3 个月,1~3 人)
MyApp/
├── Controller/
├── View/
├── Model/
├── Tool/
├── Network/
└── Resource/
特点:简单的 MVC 分层,所有文件平铺,快速迭代。问题:业务多了之后文件混乱,找不到代码。
阶段二:成长期(3~12 个月,3~8 人)
MyApp/
├── Main/ # 按业务模块分
│ ├── Discover/
│ │ ├── Controller/
│ │ ├── View/
│ │ └── Model/
│ └── ...
├── Expand/ # 基础层
├── Vender/
└── Resource/
特点:采用"业务为主"的结构一,每个业务模块内部 MVC 分层。问题:业务模块间耦合严重,公共类归属模糊,编译越来越慢。
阶段三:成熟期(1 年以上,8 人以上)
MyApp/ # 壳工程
├── App/
├── Modules/ # 业务组件(独立 Pod)
├── BasicModules/ # 基础组件
└── Podfile
特点:组件化架构,每个业务模块独立成 Pod,基础能力下沉为通用组件。优势:模块解耦、独立编译、团队并行开发效率高。
不要跳级:初创期不要直接上组件化,成本太高、维护不起。先把目录规范、模块边界做好,等团队和项目规模到了再逐步组件化。
六、命名规范与注释建议
1. 目录命名规范
| 规范 | 说明 | 示例 |
|---|---|---|
| 英文为主 | 目录名用英文,避免中文路径导致的编译问题 | Discover 而非 发现 |
| 首字母大写 | 目录名单词首字母大写(UpperCamelCase) | FriendCircle 而非 friendCircle |
| 业务模块名 | 用业务含义的英文单词或词组 | Discover、Follow、Me |
| 架构层名 | 用标准架构术语 | Controller、View、Model、ViewModel |
| 可加中文注释 | 中小项目可以加中文注释方便理解 | Discover(发现) |
| 避免缩写 | 除非是通用缩写(如 VC、VM),否则用完整单词 | ViewController 而非 ViewCtr |
2. 文件命名规范
| 文件类型 | 命名格式 | 示例 |
|---|---|---|
| 控制器 | 业务名+ViewController |
DiscoverViewController |
| 视图 | 业务名+View/Cell/HeaderView |
DiscoverCell、DiscoverHeaderView |
| 模型 | 业务名+Model |
DiscoverModel |
| ViewModel | 业务名+ViewModel |
DiscoverViewModel |
| 工具类 | 功能+Tool |
DateTool、StringTool |
| 管理器 | 功能+Manager |
HttpRequestManager |
| 分类 | 类名+功能 |
UIView+Frame、NSString+Hash |
| 基类 | Base+类型 |
BaseViewController、BaseView |
3. 关于中文注释的讨论
原文提到了"目录备注中文名感觉 lo"的讨论,这里给出客观建议:
| 场景 | 建议 |
|---|---|
| 个人项目 / 小团队 | 可以加中文注释,降低理解成本,快速上手 |
| 中大型团队 / 开源项目 | 纯英文目录名,通过 README 或文档说明每个目录的职责 |
| 代码中的注释 | 中文注释是必要的,尤其是复杂业务逻辑,方便自己和他人维护 |
| 变量和方法名 | 用英文,不要用拼音或中文翻译的生硬命名,命名不好时加注释说明 |
核心原则:注释的目的是降低理解成本。如果团队成员都能看懂英文,纯英文更专业;如果有新人或非技术人员需要理解项目结构,加中文注释更友好。没有绝对的对错,适合团队的就是最好的。
七、Swift 项目目录特点
Swift 项目和 OC 项目的目录结构基本一致,但有一些 Swift 特有的点:
1. 没有 .h/.m 文件
Swift 只有一个 .swift 文件,没有头文件和实现文件的分离,文件数量少一半,目录更简洁。
2. 多了一些 Swift 特有的目录
MyApp/
├── MyApp/ # 主 Target
│ ├── App/
│ │ ├── AppDelegate.swift
│ │ ├── SceneDelegate.swift # iOS 13+ 的场景代理
│ │ └── MyAppApp.swift # SwiftUI 项目的 App 入口
│ ├── Main/
│ ├── Expand/
│ └── Resource/
├── MyAppTests/ # 单元测试 Target
├── MyAppUITests/ # UI 测试 Target
├── Packages/ # Swift Package Manager 依赖(如果用 SPM)
├── MyApp.xcodeproj/
├── Podfile # 如果用 CocoaPods
└── Package.swift # 如果用 SPM 管理自己的模块
3. Swift 模块组织
Swift 可以用 import 导入模块,组件化项目中每个组件是一个 Swift Module,目录结构和 OC 组件化类似,但用 Package.swift 或 .podspec 管理依赖。
4. SwiftUI 项目
SwiftUI 项目的目录更简洁,没有 ViewController 层(换成了 View),架构通常是 MVVM:
MyApp/
├── App/
│ └── MyAppApp.swift # @main 入口
├── Views/ # SwiftUI 视图
│ ├── Discover/
│ └── ...
├── ViewModels/ # 视图模型
├── Models/ # 数据模型
├── Services/ # 服务层(网络、数据库等)
└── Resources/
八、常见问题与坑
问题 1:目录结构定了之后还能改吗?
答案:能改,但成本随项目规模递增。
- 小项目:随时可以重构目录,Xcode 中移动文件不会影响编译(只要更新 group 引用)。
- 中大型项目:重构目录需要大量修改
#import路径、更新 Podspec、处理冲突,建议在版本迭代间隙逐步调整,不要一次性大改。 - 最佳实践:项目初期就规划好目录结构,后续小步调整,避免大规模重构。
问题 2:Xcode 中的 Group(黄色文件夹)和 Folder(蓝色文件夹)有什么区别?
| 类型 | 图标 | 说明 | 适用场景 |
|---|---|---|---|
| Group | 黄色文件夹 | 仅 Xcode 中的逻辑分组,不对应真实磁盘目录,文件编译时会被打平 | 代码文件(.h/.m/.swift),推荐用 Group |
| Folder | 蓝色文件夹 | 对应真实磁盘目录,文件保留目录结构,通过路径访问 | 资源文件(图片、H5、配置文件),需要保留目录结构时用 |
注意:
- 代码文件推荐用 Group(黄色),因为 Group 不影响
#import路径,移动文件不需要改引用。 - 资源文件如果需要按目录路径访问(如
bundle pathForResource:@"index" ofType:@"html" inDirectory:@"web"),必须用 Folder(蓝色)。 - 很多团队的做法是:Xcode 中用 Group,但磁盘上也建对应的真实目录,保持一致(通过 "Create groups" + 勾选 "Copy items if needed",并在磁盘上手动建对应目录)。
问题 3:公共类到底该放哪里?
答案:按"被谁使用"决定:
- 被多个业务模块使用:放
Expand/(基础层)下的对应目录,如通用工具放Expand/Tool/,通用视图放Expand/Base/或Expand/UI/。 - 只被一个业务模块使用:放该业务模块目录下的
Other/或View/中。 - 不确定未来会不会被复用:先放业务模块下,等有第二个模块使用时再下沉到基础层。不要过度设计,不要一开始就把所有类都放基础层。
问题 4:组件化项目中,资源文件怎么管理?
答案:每个组件自己管理自己的资源,放在组件目录下的 Assets/ 中,通过组件的 bundle 访问。主工程的资源放主工程的 Assets.xcassets。
// Swift 中访问组件内的图片
let image = UIImage(named: "icon_discover", in: Bundle(for: DiscoverModule.self), compatibleWith: nil)
组件化资源管理是个大坑,建议用
resource_bundles(CocoaPods)或resources(SPM)规范管理,避免资源名冲突和找不到资源的问题。
问题 5:目录层级太深怎么办?
答案:控制在 3~4 层以内。
- 推荐:
Main/Discover/Controller/(3 层)。 - 不推荐:
Main/Business/Social/Discover/List/Controller/(6 层,找文件太累)。 - 如果业务模块特别多,可以在
Main/下再按业务域分一层(如Main/Social/Discover/、Main/Trade/Order/),但最多再加一层。
问题 6:你们项目的目录结构
回答框架:
- 整体分层:我们项目采用"业务为主"的结构,分为业务层、基础层、第三方、资源层四大块。
- 业务层细节:业务层按业务模块分(发现、关注、我的等),每个模块内部按 MVVM 分层(Controller、View、ViewModel、Model)。
- 基础层内容:基础层包含网络、工具、分类、数据库、基类、路由等通用能力。
- 为什么这么分:业务聚合度高,便于团队按业务线分工,也为后续组件化拆分做准备。
- 遇到的问题和改进:比如公共类归属模糊,我们的做法是先放业务模块,被复用时再下沉;项目大了之后我们做了组件化拆分,每个业务模块独立成 Pod。
九、总结
- 目录结构没有绝对的好坏,适合项目规模、团队习惯、业务特点的就是好结构。
- 两种主流结构:
- 结构一(业务为主):主目录按业务分,内目录按 MVC 分——推荐中大型项目。
- 结构二(分层为主):主目录按 MVC 分,内目录按业务分——推荐小型项目。
- 标准四层架构:业务层(Main)、基础层(Expand)、第三方(Vender)、资源层(Resource)。
- 架构影响目录:MVC 三层、MVVM 加 ViewModel、VIPER 五层、组件化独立 Pod。
- 演进路径:初创期简单 MVC → 成长期业务为主 → 成熟期组件化,不要跳级。
- 命名规范:英文为主、首字母大写、业务含义清晰,中小项目可加中文注释。
- 面试回答:先整体分层、再业务细节、再基础层、再设计原因、再问题改进。
目录结构是项目的"第一印象",也是团队工程规范的体现。花时间规划好目录结构,不是浪费时间,而是为后续的开发效率、可维护性、团队协作打下基础。

浙公网安备 33010602011771号