惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

S
Secure Thoughts
C
Cybersecurity and Infrastructure Security Agency CISA
T
Tenable Blog
Project Zero
Project Zero
T
The Exploit Database - CXSecurity.com
T
Threat Research - Cisco Blogs
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Cyberwarzone
Cyberwarzone
PCI Perspectives
PCI Perspectives
G
GRAHAM CLULEY
H
Hacker News: Front Page
Cloudbric
Cloudbric
Latest news
Latest news
N
News and Events Feed by Topic
C
CERT Recently Published Vulnerability Notes
Attack and Defense Labs
Attack and Defense Labs
SecWiki News
SecWiki News
Security Latest
Security Latest
MyScale Blog
MyScale Blog
阮一峰的网络日志
阮一峰的网络日志
Vercel News
Vercel News
The GitHub Blog
The GitHub Blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Security Archives - TechRepublic
Security Archives - TechRepublic
V2EX - 技术
V2EX - 技术
B
Blog RSS Feed
L
LINUX DO - 最新话题
人人都是产品经理
人人都是产品经理
Last Week in AI
Last Week in AI
IT之家
IT之家
Jina AI
Jina AI
Y
Y Combinator Blog
博客园 - 聂微东
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
V
Visual Studio Blog
P
Privacy International News Feed
B
Blog
S
Schneier on Security
Application and Cybersecurity Blog
Application and Cybersecurity Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Hacker News - Newest:
Hacker News - Newest: "LLM"
Help Net Security
Help Net Security
D
DataBreaches.Net
博客园_首页
G
Google Developers Blog
I
InfoQ
量子位
大猫的无限游戏
大猫的无限游戏
S
Security @ Cisco Blogs

土土哥的Blog

好久没有更新博客=。= 阿里巴巴国际无线技术部 - 招人啦~求iOS、Android、Java 转-阿里巴巴国际无线技术部 - 在这里遇见最好的自己 反编译分析并模拟实现methodSignatureForSelector方法 反编译分析Xcode8的Bug, release下连续两次调用有二级指针参数的空方法会Crash 有趣的Autolayout示例5-Masonry实现 在对象dealloc的后期执行Task-开源库TTGDeallocTaskHelper 用QuartzCode快速实现一个收藏动画 开源项目-拼图验证控件TTGPuzzleVerify的实现 Swift开源Mac App - BingWallPaper 有趣的Autolayout示例4-Masonry实现 翻译-为什么objc_msgSend必须用汇编实现 API返回结果设计经验与总结 结合访问Out Parameters出现EXC_BAD_ACCESS的例子,反编译汇编解读__autoreleasing 对组件化与模块化的思考与总结 开源项目-TTGTagCollectionView 有趣的Autolayout示例3-Masonry实现 Swift开源项目: TTGEmojiRate的实现 Swift写的库-TTGSnackbar 有趣的Autolayout示例2-Masonry实现 解决iOS项目的版本兼容问题-结合宏、Category和Runtime 用Runtime的手段填充任意NSObject对象的nil属性 有趣的Autolayout示例-Masonry实现 UITextView编辑时插入自定义表情-续-自定义表情图片的大小 RPC框架Thrift例子-PHP调用C++后端程序 GCD使用经验与技巧浅谈 为GCD队列绑定NSObject类型上下文数据-利用__bridge_retained(transfer)转移内存管理权 Enum-枚举的正确使用-Effective-Objective-C-读书笔记-Item-5 @autoreleasepool-内存的分配与释放 有关宏定义的经验与技巧-简化代码-增强Log Effective-Objective-C-读书笔记-Item-4-如何正确定义常量 UITextView编辑时插入自定义表情-简单的图文混编 关于评论不见了=。= Entity和Model的不同-关于代码的数据层 一次审核被拒的经历-关于iCloud到底应该备份什么数据 Block类型变量-缓存Http请求与回调 提升UITableView性能-复杂页面的优化 NSString的Copy与内存分配 利用NSProxy实现消息转发-模块化的网络接口层设计-原创 Effective-Objective-C-读书笔记-Item-3 Effective-Objective-C-读书笔记-Item-2 Effective-Objective-C-读书笔记-Item-1 iOS项目的目录结构-原创 Android开源库-LinkTextView-原创 第一篇Blog
总结一些iOS项目中组织代码的方法
土土哥 · 2016-04-10 · via 土土哥的Blog

前言

总结一些组织代码的方法,大到各个子项目模块之间,小到单个文件内部,涉及到了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'
source 'https://git.coding.net/xxx/specs.git'


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


#pragma mark - Public methods


#pragma mark - Actions

- (void)didTapCancelButton:(UIButton *button)


#pragma mark - Notifications - XXX


#pragma mark - UITableViewDelegate


#pragma mark - XXXDelegate


#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”

参考