总结一些iOS项目中组织代码的方法

前言

总结一些组织代码的方法,大到各个子项目模块之间,小到单个文件内部,涉及到了CocoaPods私有库、Carthage构建私有Framework、项目内Group和文件夹、类内用Category组织、文件内的#pragam mark、// MARK -。虽然有些老生常谈,就当是重新梳理一遍吧=。=。

多个子项目 - CocoaPods加私有仓库

感觉目前CocoaPods已经成了搞iOS开发的标配了,基本上所有的第三方库和组件都可以通过CocoaPods来集成管理。但是CocoaPods的功能其实不止管理第三方库和组件,团队内部的拆分出来的子项目和子模块,同样可以用CocoaPods来集成管理。

先看看如下Podfile的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 源配置 #
source 'git@gitlab.xxx.com:cocoapods/specs.git' # 团队内部Gitlab源
source 'https://git.coding.net/xxx/specs.git' # Coding等私有Git托管平台

# iOS版本支持 #
platform :ios, '7.0'

# 开发中的子项目 #
pod 'XXXCommonUI', :path=> '/Users/tutuge/projects/Xcode/XXXCommonUI' # 本地路径

# 依赖的内部子项目 #
pod 'XXXModuleA', :git=> 'git@gitlab.xxx.com:cocoapods/XXXModuleA', :branch=>'develop'
pod 'XXXModuleB', '1.0.1'

# 依赖的开源第三方库 #
pod 'AFNetworking','3.0.0'
pod 'DTCoreText'

# 额外的脚本 #
# ...

私有Git仓库

虽然我们有万能的Github了,但是还是有些代码是不能公开的=。=,所以需要一个私有的Git仓库。

有自己服务器的,完全可以用Gitlab(或者Gogs)搭建一个功能强大的私有Git仓库,管理项目代码,搭建CocoaPods私有源都很不错。

当然,懒得折腾的,也可以用第三方的私有Git服务,如:BitbucketCoding等。

私有CocoaPods源

搭建一个私有CocoaPods源其实就是创建一个私有的Spec Repo,说白了就是一个存放了所有私有Pods的Podspec文件的,按照特定结构组织的目录,然后把这个目录推到私有Git仓库就成了一个私有的CocoaPods源。

Spec Repo的目录结构示例如下:

1
2
3
4
5
6
├── Specs
└── [XXXModuleA]
└── [0.0.1]
└── [XXXModuleA].podspec.json
└── [0.0.2]
└── [XXXModuleA].podspec.json

然后添加私有源即可:

1
pod repo add XXXSpecRepo https://git.coding.net/xxx/specs.git

创建私有CocoaPods项目的具体步骤

至于具体的创建步骤,CocoaPods的官方文档和一些优秀的博客已经说得很清楚了,我就不再重复了。推荐阅读:CocoaPods Guides,还有使用Cocoapods创建私有podspec,这篇很详细!

引用正在本地开发的子项目

主工程依赖的子项目也是可以同时开发的,用path指定路径即可,如pod 'XXXCommonUI', :path=> '/Users/tutuge/projects/Xcode/XXXCommonUI',然后pod install一下,依赖的本地工程就会集成到主工程的Development Pods目录下,开发时,就可以同时修改主项目和子项目了。

构建私有Framework - Carthage

除了抽离出子项目,用Framework的方式来管理公共代码也是不错的选择,Framework的好处也是多多的:

  • 本质上是个bundle,不仅可以打包代码,还可以打包资源Assets、视图XIB等
  • 可以有选择的开放头文件、接口,避免了用CocoaPods集成子项目时,所有代码全部暴露的问题,只暴露需要的,代码更加清晰。
  • 从iOS 8开始支持的Cocoa Touch Framework可以支持动态Framework,能在主App和各种Extension之间共享一份二进制目标代码,加快应用加载速度,减小体积,加快编译速度等。

基于Cocoa Touch Framework动态框架的种种优点,“去中心化”的轻量级依赖包管理工具Carthage也越来越受欢迎。

总的来说,Carthage没有CocoaPods那样的Spec Repo中心,不会对现有项目工程修改,只是完成了拉取代码、构建Framework的工作,非常的轻量级,所以用来构建团队内公用的Framework非常不错~

创建Framework

这里的Framework指的是Cocoa Touch Framework动态框架,静态Framework的缺点不少(如不能共享已加载到内存的二进制目标代码、构建麻烦等),如果项目可以从iOS 8开始支持,为啥不用动态框架呢~

创建Framework的过程非常简单,Xcode 6以后就有了Framework项目模板,直接选中”Cocoa Touch Framework”模板,按照提示来即可。记得在TARGETS->Build Phases->Headers->Public"加入要暴露的头文件(Swift的话,将要暴露的类、方法标记public)就好。

用Carthage构建Framework

Carthage只支持构建标记为“Shared”的项目Target,所以创建好了Framework工程后,还要设置Scheme为“Shared”,可以在当前Scheme的Edit Scheme里面设置,也可以在Xcode的菜单Product->Scheme->Manage Schemes里面设置。

然后就可以将Framework工程推送到Git仓库中,可以是Github,也可以是私有Git仓库,如上一节提到的Coding、Bitbucket等,只要本机可以正常访问即可。记住打上版本号tag。

然后就可以在一个测试工程中创建一个Cartfile文件,输入Framework的Git路径,按照官方文档设置一下”Run Script”,运行carthage update生成对应的Framework,就可以集成测试。

最终的Cartfile

由于Carthage的去中心化,用Carthage管理项目的Framework依赖就非常灵活,一个简单的示例如下:

1
2
3
4
5
6
7
8
# 开发中的本地Framework
git "file:///directory/to/project" "develop"

# 依赖的私有Framework
git "git@gitlab.xxx.com:cocoapods/XXXFramework.git" "development"

# 第三方开源库
github "Mantle/Mantle" ~> 1.0

所以,Carthage可以方便的管理构建本地、私有、第三方的Framework库,而且更加方便~

详细的创建流程Google一下就有很多,如Build your own Cocoa Touch Frameworks, in pure SwiftCreating your first iOS FrameworkHow to Make a Carthage-compatible Framework in Swift

和CocoaPods混用

Carthage是没有任何侵入性的,不会对项目工程有修改,只是构建出了Framework文件,所以完全可以把Carthage当做纯粹的Framework构建工具,跟CocoaPods一起愉快的使用=。=

项目内文件组织 - Group和文件夹

项目内的代码组织,其实就是代码文件的组织,总的来说,其实有以下三种:

  1. 具有物理文件夹结构的Folder reference
  2. 从Xcode创建的逻辑上的Group
  3. 具有物理文件夹结构的Group

排除Folder reference

Folder reference说白了就是硬盘上一个文件夹的逻辑引用,有点像Linux文件系统里面的软连接=。=,图标是蓝色的,将一个文件夹拖到Xcode中时,Create folder references选项创建的就是。

虽然编译时也会被打包到程序中,更能同步真正文件夹内部的修改,但是遗憾是的里面的代码文件是不能自动的加入到Xcode的编译中的,要手动一个一个加,而且图片资源也不能在IB里面使用,代码里面使用也要加上路径,所以一般是纯资源文件夹用Folder reference的方式引用,对于组织代码文件,就可以排除Folder reference的方式。

纯逻辑Group文件平铺,脱离Xcode不方便查看

首先要明确的是,Group是Xcode对文件的一种逻辑上的引用,并且在编译时Xcode会把所有的Group展开,所以代码里面也就不用像Folder reference一样加上路径,还可以针对Target作区分,很方便。

但是Xcode中创建的Group并没有对应到文件系统中的文件夹,导致一旦脱离Xcode看代码,所有的文件都平铺在了根目录下,非常不方便。

难道没有办法了?

有物理文件夹结构的Group最好

还有一种Group是可以有物理文件夹结构的,其实将一个文件夹拖到Xcode中,选择Create groups for any added folders创建的Group就是既有Group的有点,又可以保持物理文件夹结构。这样无意是最好的~

手动创建:
可以Add Files To XXXX,然后选择New Folder创建文件夹,然后把代码文件拖进去,再添加回Xcode。

自动创建:
对于现有的大量的Group,手动一个一个创建实在不方便,还好有个命令行工具:synx,直接在工程根目录运行synx XXX.xcodeproj就可以自动为Group创建物理文件夹。
当然,CocoaPods管理的库要重新pod install一次,手动添加的Framework也要重新添加。

类内代码组织 - Category

Objective-C中的Category实在是太方便了,可以灵活的为现有的类增加方法,甚至是系统的类。

但是其实Category也可以拿来组织一个类的实现,将类的Property、方法按照业务、功能拆分到不同的Category里面,使类的结构更加清晰。

系统API本身就有大量的例子,如UIViewController的:

1
2
3
4
5
6
7
@interface UIViewController (UIContainerViewControllerCallbacks)
// ...
@end

@interface UIViewController(UIViewControllerTransitioning)
// ...
@end

实现的时候还可以每个Category对应一个头文件和.m文件,类的结构更加清晰:

1
2
3
4
5
6
XXXClass.h
XXXClass.m
XXXClass+XXXModuleA.h
XXXClass+XXXModuleA.m
XXXClass+XXXModuleB.h
XXXClass+XXXModuleB.m

文件内代码组织 - #pragam mark、// MARK -

整理文件内的代码时,最好的手段就是用#pragam mark宏定义,Swift的话就是// MARK -

Objective-C冗长的语法注定了一个代码文件的行数不会少=。=,如果没有一定的规范,实在是难以迭代,所以最好就是开发前约定一种规则,对主要的类的实现都约定出一套规范,如UIViewController的子类、UIView的子类、UITableViewCell的子类等,然后开发人员按照这个规范写代码,这样才能使代码更好维护。

如,UIViewController的子类实现时,可以按照如下顺序组织代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
@implementation XXXViewController

// 内存相关
#pragma mark - Memory manager

- (instancetype)init
- (void)dealloc

// 类生命周期相关的
#pragma mark - Life cycle

(void)viewDidLoad

// 类的Public方法
#pragma mark - Public methods

// 所有的Actions
#pragma mark - Actions

- (void)didTapCancelButton:(UIButton *button)

// 通知回调,具体可以细分
#pragma mark - Notifications - XXX

// 系统的Delegate
#pragma mark - UITableViewDelegate

// 自定义类的Delegate
#pragma mark - XXXDelegate

// 自定义View、初始化等
#pragma mark - Custom views

// 类私有方法
#pragma mark - Private methods

@end

当然,具体的组织方法还要根据项目本身的架构、模式决定,上面的只是参考。

进一步:创建项目专属文件模板

既然定下了文件内的组织顺序,每次写代码手工输入也还是有点麻烦,所以更进一步的话就是创建自定义的Xcode文件模板,把不同类的#pragma mark顺序规则直接写到模板里面,以后开发人员创建文件时直接用项目专属的模板就可以了~

具体的创建Xcode模板的步骤不在本文范畴=。=,不过倒是发现了一个Xcode插件,专门用来创建模板的:Stencil: A plugin for Xcode allowing you to easily create custom file templates,以及对应的文章:Introducing Stencil,还有一些教程:How to Create Custom Project Templates in Xcode 7Creating Custom Xcode Templates,读者可自行阅读~

最后

一切为了更加干净整洁的代码,“May the clean code be with you”

参考

当前网速较慢或者你使用的浏览器不支持博客特定功能,请尝试刷新或换用Chrome、Firefox等现代浏览器