iOS项目重构(AppDelegate、基类、Const)



一、背景

公司的项目到现在已有 8 年的开发时间了,相较上技术发展之快,里面有很多不合理的地方也不足为奇。开发起来困难且影响速度,所以我们在一步步的改进着。

前期工作:

六月份的时候,我们对项目进行了项目目录上的整理。按照模块、功能划分所有文件,删除了很多不再使用的文件,整理了很多虚拟目录和实际文件位置问题。对后面的开发使用效果出乎意料的重要,真正体会到一个好的项目目录结构是一个App重要的开端。这半年来的开发过程中,也在不断完善目录的设计。具体可以参考Objective-C开发规范(包括项目目录结构)

提到这呢,一是想强调项目目录的重要性,二是因为这次的重构也涉及到了目录的改动,更新了之前的文档,希望大家了解到。

本次工作:

尝到了好的目录的甜头,继续我们的优化之路。这次呢,我们将改进重心放到了重要模块的重构。

AppDelegate 是每个iOS项目的核心,是整个 App 的入口,它确保应用程序和系统以及和其他应用程序之间的交互,所以 AppDelegate 通常会承担很多职责。而因为它的唯一性和一直存在的特征,很多方法和功能放在这里特别方便使用,这更加重了 AppDelegate 的负担,最终导致了Massive AppDelegate

Template 是每个页面都要继承的父类,除了统一的页面配置,还有为了方便某些页面使用的同个功能都放在了这里。在修改某个功能时,一点改动都牵扯所有页面;而且继承的缺点使得开发者必须要了解父类,庞大的父类无疑增加开发的成本,之前就有过某个页面使用了和父类一样的属性而导致的 Bug。

Const是全局使用的配置文件,之前架构的不合理导致为了方便使用的定义就都放在 Const 中,导致 Const 庞大且混乱。


综上所述,在复杂的 AppDelegate 和 Template 里修改任何东西的成本都是很高的,因为它将会影响整个APP,且包括 Const 的全局性,都增加了文件编译时的复杂度和性能问题。毫无疑问,这三个模块的简洁和清晰对一个好的 iOS 架构来说至关重要

因为这它们的改动将会影响整个 App 的,所以既然是必须要做的改动,我们就用一个版本的开发时间将这三个模块一起改动,这样也少给测试同学增加麻烦嘛~✌️

二、重构的方法

2.1 整理所有方法并划分功能块

AppDelegateTemplateConst因为方便使用,所以里面被放很多不属于它们的功能。

例如:

  1. AppDelegate,包含配置SDK、网络异常弹框、用户自动登录等;
  2. Template,包含部分页面都用到的属性名、支付功能、地图定位功能等;
  3. Const,包含有各个模块的枚举、埋点名称、通知名称等。

所以整理某一块的时候,首先整理里面的所有方法,然后按照功能划分几个功能块。

2.2 思考功能块去处

例如,AppDelegate中:

  1. 十几个SDK的配置方法,应该单独有一个SDK配置类去统一管理;(配置)
  2. 关于网络的弹窗,应该拿到弹窗类中管理调用;(工具)
  3. 用户自动登录,应该在用户信息管理中。(业务)

这些配置、工具类方法、业务功能,还有很多属性、方法都不属于 AppDelegate,是为了使用方便而放在这的。

2.3 思考功能块用法

功能块的去处要么是在放在它本该去的类中,要么是单独创建一个类。单独创建的类就要考虑是用单例还是类方法,或是使用分类

例如,AppDelegate中:

  1. 需要App启动后立马调用的、功能间没有太大关系的一系列方法,使用类方法即可;
  2. 而地图定位功能就需要使用单例了;
  3. AppDelegate本身方法的实现使用分类。

三、AppDelegate

重构前:.h:126 行;.m:1909 行
重构后:.h:16行;.m:84 行

重构后的AppDelegate.h

#import <UIKit/UIKit.h>

@interface AppDelegate : UIResponder <UIApplicationDelegate>

@property (nonatomic, strong) UIWindow *window;

@end

重构后的AppDelegate.m

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // 配置APP运行环境
    // 配置SDK
    // 业务相关(需要启动后马上要做的操作)
    return YES;
}

最终主要目录结构如下:

|- AppDelegate      // 单独一个文件夹   
    |- AppDelegate  // 入口.h/.m文件
    |- Category     // AppDelegate的扩展
        |- AppDelegate+EHI3DTouch
        |- AppDelegate+EHIOpenURL
        |- AppDelegate+EHIRemoteNotification
    |- StartLanch   // App启动后需要的业务逻辑
        |- EHIStartLanchManager
    |- SDKManager   
        |- EHIVendorSDKDefines.h // SDK的key或ID等定义
        |- EHIVendorSDKManager   // 第三方SDK配置管理
        |- EHIMapManager         // 地图管理
        |- EHIPushManager        // 推送管理

最终AppDelegate.h保留了项目初建时的样子,不提供外部多余的属性和方法。

  1. 本身功能的扩展;
  2. App启动后需要调用的业务逻辑;
  3. SDK配置管理,重要SDK的封装使用。
  4. 其他较为散的功能放在对应类中。例如:城市信息放在城市数据管理类中;
  5. 模块功能封装后放在对应的模块文件夹下。例如:下单流程数据操作单独封装,放在Data模块的文件夹下。

总之,主要本着单一职责原则,按职责划分来处理。

四、Template

重构前:.h:292 行;.m:1256 行。
重构后:.h:21行;.m:53 行。

重构后的Template.h
不提供任何属性和方法,只引入了必须的头文件。如:网络请求的头文件。

重构后的Template.m
只实现了系统本身的生命周期方法。处理:导航栏设置、页面统计。

最终主要目录结构如下:

|- Base // 单独一个文件夹  
    |- Template      // .h/.m文件
    |- NavigationBar // 导航栏处理
        |- UIViewController+EHINavigationBar

功能整理如下:

  1. 老逻辑方法的删除。例如删除旧版的微信支付方法、相关文件,连带Html5页面回调的处理确定不会使用后,删除相关方法;
  2. 支付、地图、定位、登入登出模块功能分别封装;
  3. 为了方便写的属性,分别写在各个需要的ViewController中;
  4. 为了方便写的方法,找到各自去处。例如:字符串判断放在NSString的扩展类中;
  5. 导航栏设置单独在分类中实现;
  6. 作为基类,导入头文件的控制。

五、Const

重构前:.h:1035 行;.m:14 行。
重构后:.h:56行。

最终主要目录结构如下:

|- Defines                // 整个应用会用到的定义
    |- EHIMacro.h         // 必用头文件引入(YYKit.h)
    |- EHIAppDefines.h    // App信息、UI的宏定义(字体、颜色、适配)
    |- EHIFunctionDefines // 全局方法或内敛函数定义(EHIWeakSelf/EHIStrongSelf)
    |- EHIEventDefines    // 统计事件使用的Key

从目录中可以看出来:

  1. 主要是统计事件使用的Key定义,App信息、UI的定义,全局方法的定义三大块;
  2. EHIMacro.h作为宏控制在.pch中全局引入的头文件,只引入一些必须的,不必须的各个文件用的时候再引用;
  3. 其他的枚举、宏定义、常量值等都在对应的模块中分别创建Defines文件处理。例如:订单中心的EHIOrderCenterDefines

这部分的整理中,因为对只创建.h文件和也创建.m文件引起了争议,所以扩展了iOS常量使用extern和static原理探究,为什么Defines中默认推荐使用extern?这篇博客,有兴趣的可以看一下。

六、总结

  1. 不要为了方便把属性、方法、功能逻辑放在AppDelegateTemplate中;
  2. 不要为了方便部分类把定义的间距或尺寸放在Const中,要写模块Defines或使用extern使多个文件共用;
  3. 加全局使用的东西之前检查是否已经有可用的;
  4. 不用的东西要删除:包括类文件、方法、属性、引入的头文件;
  5. 少用单例;
  6. 少用BOOL属性,同级判断用枚举;
  7. 再次强调单一职责原则

嗯。。。这次任务很多很辛苦,但是学习到了很多,开心。✌️

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容