一、文件系统
- 总则
- 文件的物理路径和逻辑路经保持统一
- 每个业务文件夹下都是MVC大结构
- 如果有支持性的VC,这个VC写在和View、Model平行的文件夹Vendor文件夹里面
- 公用的文件放在最外面
- 文件命名
- 文件夹及文件首字母大写,符合驼峰命名规范
- 业务类不需要前缀(PT、HDF,PT是用户维护通用的前缀),如果是公用的非业务层需要加前缀,依据作用范围使用PT或者HDF作为前缀
- 数据Model以Model结尾
- 业务Model以Manager、Handler、Helper等结尾
- 宏文件以Macro结尾
- 文件顺序
- 业务文件夹下依次是大ViewController、Model、View
- View文件夹里面顺序是按照页面布局自上向下罗列,如果单个区域view嵌套多可以建一个文件
- 资源文件
- 图片文件都放在 images.xcassets里
- 文件结构
- 一级目录:医生品牌
- 二级目录:点评、服务星(业务)
- RTF文本文档放在业务文件夹最上面的层,打开业务文件夹就能看到
- 命名
- 都以“yspp_”开头
- 二级前缀依据图片大小和用途分4个二级前缀yspp_btn_、yspp_icon_、yspp_image_、yspp_screen_ (全屏的图)。
文件结构:
资源文件结构:
二、代码结构
- 总则
- 声明文件 ( .h 文件)中不能声明实例变量
- 实现文件 ( .m 文件)中不声明属性
- #import 相关
- #import 都写在.m文件里, .h文件用@class 引用。(.h 只是声明)
- #import不能有重复和没有用到的
- #import做小的分区分区,分别是:1.自己的View、Model类、2.其他有交互的类、3.工具、类目等
- 所有方法名都小写,有前缀的,需把前缀小写加下杠线。(如HDFActionSheet, hdf_actionsheetDidSelected...)
- 方法命名不留and 以一个流利的英文句子为基准
- 如果方法参数有集合类型的,用泛型标记
- (nullable NSArray<UITableViewRowAction *> *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(8_0)__TVOS_PROHIBITED; // supercedes -tableView:titleForDeleteConfirmationButtonForRowAtIndexPath: if return value is non-nil
- 声明文件 ( .h 文件)
- 顺序(自上向下)
- 1.#import
- 2. 枚举
- 3. 宏
- 4. 常量
- 5. delegate声明
- 6. 属性
- 7. 方法
- 8. delegate实现
- 属性
- .h 属性不声明可变对象,可变对象放在 .m实例变量里声明,如果外面需要读取,封装个getter方法,返回不可变对象。
-
@interface DoctorHonoursNetwork : NSObject
- (void)addItem:(NSObject *)a;
- (NSArray *)mArray; -
@interface DoctorHonoursNetwork ()
{
NSMutableArray *_mArray;
}
-
@interface DoctorHonoursNetwork : NSObject
- 除“)”左边,声明语句每个单词或逗号后面都加空格,(顺序:内存管理语义-原子性-读写属性) @property (nonatomic, assign) NSString *doctorId;
- 属性是不可变对象都用Copy属性。
- 可变的属性(Mutable)需要原子操作(atomic)
- 属性的注释推荐使用方式:/**注释内容*/,这样写在点语法时可以直接展示注释
- .h 属性不声明可变对象,可变对象放在 .m实例变量里声明,如果外面需要读取,封装个getter方法,返回不可变对象。
- 方法做分区
- 如果共公方法多的话做小的功能分区。
- delegate
- 在@interface之前声明,在@end之后实现
- 方法名都以类名开头(小写),并且自身作为必传参数传回 (- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;)
- - (void) viewDidDisplay:(UIView *)selfView;
- (void) view(UIView *)selfView didDisplay; (不正确的写法)
- - (void) viewDidDisplay:(UIView *)selfView;
- 每个方法必须传回自身
- delegate必须是weak,避免循环引用
- 顺序(自上向下)
- 实现文件( .m 文件)
- 实例变量
- 只在 .m的延展里声明实例变量 ( @interface NSObject () )
- 命名都以 “_”开头
- 直接用实例变量赋值,如果被赋值对象是NSString、NSArray ,且赋值对象是可变的类,用copy方法赋值。
- 生命周期方法结构
- #pragma mark - life cycle
- dealloc 最上面
- 初始化方法
- 按照页面展现生命周期写 (willAppear、didAppear、willDisappear、DidDisappear)
- #pragma mark - life cycle
- 实例变量
- #pragma mark - delegates
- #pragma mark -- delegate1 (注意第二层中间是两个杠)
- #pragma mark -- delegate2
- #pragma mark - event response
- 方法名以Action结尾 如:backAction、rightItemAction、handleAlertAction
- #pragma mark - public methods
- .h中声明的方法
- #pragma mark - private methods
- 命名以p_开头 如:p_methodA
- 延展里声明一下
- #pragma mark -- UI
- #pragma mark - getter/setter
- getter 省略get
- BOOL类型的 前面加is (isShowed)
- #pragma mark - network methods
- #pragma mark - delegates
声明文件:
实现文件:( Part 1)
实现文件:( Part 2)
三、常量 宏
- 宏
- 命名
- 通用的宏常量和宏函数都大写,单词中间用“_”
- 私用的宏方法用”__“(两个下杠线开头)全部小写
- 公有的加YSPP_前缀
- 私有的确保唯一性,以类名开头,避免覆盖定义
- 宏函数参数必须用括号括起来(宏只是文本替换)
- 文件
- 如果该业务的宏比较多,需要单独建文件,以Macro结尾。放在最大作用域*文件夹下面。
- 命名
- 常量
- 命名
- 通用的以 FOUNDATION_EXTERN 开头(FOUNDATION_EXTERN NSString * constCommentNotification )
- 私用的不需要 FOUNDATION_EXTERN 需要加 static 关键字。
- 通用的需要加类名确保唯一性
- 都以”k"开头
- 对象
- 通用的定义在.h里 ,赋值写在 .m里
- .h FOUNDATION_EXTERN NSString * const kCommentNotification;
- .m NSString * const kCommentNotification = @"CommentNotification";
- 私有的写在.m里
- .m NSString * const kCommentNotification = @"CommentNotification";
- 通用的定义在.h里 ,赋值写在 .m里
- 命名
四、布局方式 数据持久化
- 布局方式
- 写页面方式
- 纯代码
- Xib
- StoryBoard(多人开发噩梦)
- 写页面方式
- 布局方式
- 手算布局
- AutoResizing(已舍弃)
- AutoLayout
- 最终确认布局方式两种:
- 纯代码 ————> 手算布局
- Xib ————> AutoLayout (可视化)
- 布局方式
- 数据持久化
- 写文件
- 归档
- NSUserDefault数据库
- 引导图,一些小数据的存储
- 命名
- key命名 kUserDefault_…
- 输出一个Const文件,专门存储key
- 维护文档
- CoreData
- ANKeyValueCache 缓存对象,可以是多个对象(流草稿、患教草稿、点评草稿)
- 最终确认两种数据持久化方式
- NSUserDefault
- 引导图,一些小数据的存储
- 命名
- key命名 kUserDefault_…
- 输出一个Const文件,专门存储key
- 维护文档
- ANKeyValueCache 缓存对象,可以是多个对象(流草稿、患教草稿、点评草稿)
- NSUserDefault
- 最终确认两种数据持久化方式
五、网络
- 文件
- 在最大的业务文件夹写一个NetworkManager,所有业务网络请求写在这个里面。参见PatientEducationNetwork.h。
- 命名以NetworkManager结尾
- 结构
- 请求方法以每个页面为纬度分区,#pragma mark -
- .h 和.m 方法顺序一样
- 方法注释
- 说明方法用途,和必要参数说明,显而易见的参数如patientId,userId不用注释
- 注释方法用途和必要注释参数,各占一行。多个参数写在一行上。
- 方法命名
- 都以request或post开头,且是类方法(+方法)
- 接口定义
- 接口名定义用常量,写在 .m里面
- 以kAPI_开头的 const指针
- NSString *const kAPI_doctoruser_getDoctorRecommendInfo =@"doctoruser_getDoctorRecommendInfo";
- 成功失败回调都用 HDFNetResponse 作为参数。
- 失败处理
- 除非产品特殊要求,失败都应该有提示 [SVProgressHUDshowErrorWithStatus:ERROR_MSG_NONETWORK];
- 如果是大WebView页面需要做失败处理点击重新加载的页面(HDFEmptyView)
- 如果是列表页,如果数据源是空的需要处理处理点击重新加载的页面,数据源不为空只给提示ERROR_MSG_NONETWORK
文件:
六、类目、延展
- category 类目
- 命名
- 类目命名:主类名+大写字母开头的,能概括这个类目意义的单词 (NSString+SizeToFit)
- 方法命名:类目名+功能名,防止多个类目使用定义功能相同且命名相同方法,互相覆盖
- 当一个过于臃肿的大类中的方法可以根据功能类型分组,则将每一组做成一个类目
- 一个功能类目对应一个文件,一个类目就是一组相同功能类型的方法簇,不要在一个文件中写多个不同功能类目方法
- 一般情况下,不允许在类目中覆盖现有类中的方法,只在类目中扩展新方法
- 不允许在类目中定义属性,若有这种需求,定义在主类里,或采用继承解决。(不要用关联对象,不是苹果提倡的做法)
- 命名
-
extension 延展
- 类延展只写在主类的.m文件中,不允许新建文件
- 将私有方法原型写在延展中,对外隐藏。
- 延展中建议只定义私有实例变量,不定义属性
七、内存、线程
- 内存泄漏相关
- 循环引用类内存泄漏
- 禁止在成员集合变量中添加成员变量 (例:[_dataArray addObject: _button1])
- 设置代理时使用weak指针
- 使用block时,会出现两种循环引用,如果出现循环引用,在block中使用block的持有者时需要使用weak引用
- NSTimer如果设置了重复执行模式,则需要在dealloc中进行invalidate;或者使用block类型的NSTimer
- 申请了不释放类内存泄漏
- 代码中使用了通知中心,add了几个,就需要在dealloc方法中对应remove掉几个
- 使用桥接将Foundation对象转为CoreFoundation对象时,说明此对象脱离ARC管理,需手动调CFRelease释放
- 代码中使用了GCD队列,需要自己手动释放
- 自定义GCD队列group,需要自己手动释放
- 控制内存峰值
- 做tableView的时候,使用reuse机制,否则只要tableView不释放,越滑动占用内存越大
- 如果在循环中有内存操作,则在循环中嵌套autoreleasePool块
- 循环引用类内存泄漏
- 线程安全,并发数控制
- 总述:线程控制简单的情况下,优先使用GCD,因为GCD用法最简单,抽象层级最低,效率最高;线程控制复杂情况下使用NSOperation,其他的pthread和NSPthread都禁止使用;少用自定义派发队列。
- 线程安全
- 常规写法:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT), 0), ^{
//可并行执行的处理
dispatch_async(dispatch_get_main_queue(), ^{
//只能在主线程执行的处理
});
}); - 单例全部使用dispatch_once来写,目前最可靠的保证单例创建代码不可重入的方式。
- 增删改临界区数据的操作需同步,使用dispatch_sync来做,调用dispatch_sync方法自动给临界区数据加锁。
- 读临界区数据不需同步,可以异步提高效率
- 使用dispatch_barriers实现读写锁(异步读,同步写)
- 自定义DispatchQueue的名称使用应用程序ID逆序域名(dispatch_queue_create(“newPatient.haodf.com”, NULL))
- 使用dispatch_group时,添加到group中的派发任务使用相同派发的队列(效率低)
- dispatch_sync的第一个参数禁止传入当前所在队列(死锁)
- 常规写法:
- 并发数控制:
- 建议使用 GCD配合 Dispatch Semaphore ,本质上就是个带计数的锁
- 使用maxConcurrentOperationCount,本质上也是个带计数的锁
- 平时客户端编程,并发数控制在3个以下
- 同类型操作可以使用GCD开启多线程,使用Dispatch Semaphore 控制最大并发数,创建Dispatch Semaphore时,将锁计数设置为3,保证线程数<=3;
例:不同类型操作,调用一次dispatch_async就会增加一个线程,如果同一个时间片内并发线程有可能超过3个,就将线程加入dispatch_group,等待dispatch_group执行结束后,再执行第四条线程,保证线程数<=3;
八、其他
- 埋点
- 命名 UM_YSPP_…….
- 文件 MobClickEvent.h,写在最下面 分区
- 方式
- Click [MobClick event:UM_DOCTOR_SERVICESTAR_CODEPAGE_RULES];
- 页面
- - (void) viewWillAppear:(BOOL)animated {
[MobClick beginLogPageView:UM_DOCTOR_SERVICESTAR_PREVIEWPAGE];
[MobClick event:UM_DOCTOR_SERVICESTAR_PREVIEWPAGE];
} - - (void) viewWillDisappear:(BOOL)animated {
[MobClick endLogPageView:UM_DOCTOR_SERVICESTAR_PREVIEWPAGE];
}
- - (void) viewWillAppear:(BOOL)animated {
- 工程文件、证书
- 除修改证书、引用文件/库、提测修改版本号/build号、重构ARC代码,不允许修改工程文件。
- AppDelegate
- 推送
- 代码
- 都在AppDelegate didReceiveRemoteNotification 处理
- 分激活状态和未激活状态
- [UIAlertView showWithTitle:@"温馨提示" message:[[userInfo objectForKey:@"aps"]objectForKey:@"alert"] cancelButtonTitle:@"查看详情" otherButtonTitles:@[@"取消"] tapBlock:^(UIAlertView *alertView, NSInteger buttonIndex) {
//处理通知相关
}];
- 数据格式
- aps是公用参数
- module是大业务
- cm是子业务
- 代码
-
app跳转(URL Scheme)
医生版 患者版 微信 wx8afb167c8108d3a9 好大夫患者 haodfpappscheme QQ QQ41D0E0C0 支付 hdfPatientAlixPay 微博 wb3026699592 微博 weiboOAuth 好大夫患者 haodfappscheme 微信 wxc7dfdd26c004598c 好大夫医生 doctorappscheme QQ QQ06027529 QQ空间 tencent100824361
- 推送
九、版本控制、上线相关
- 版本控制
- 一些引导图版本号,只在当前版本出来,用户跳着升级不出来。
- 数据版本号,考虑数据的有效版本。
- 上线
- 如果是打上线包需要改的地方 需要加 #warning 上线
- 上线前确认:
- 搜索 #warning 上线 修改需要改的地方
- 医生版
- 注释掉所有环境宏
- 患者版
确认HDFNetworkManager 的 self.productReleasing = YES;
- 医生版
十、附录1(公共库)
序号
|
功能
|
对应库文件
|
备注
|
1
|
数据解析
|
MJExtension
|
|
2
|
网络
|
HDFNet
|
|
3
|
弹窗提示
|
SVProgressView
|
|
4
|
上拉下拉刷新
|
MJRefresh
|
|
5
|
用户信息
|
HDFUserManager
|
|
6
|
缓存
|
ANKeyValueCache
|
|
7
|
网络图片
|
SDWebImage
|
|
8
|
大图预览
|
PTImageShowViewController
|
|
9
|
蒙层引导图
|
AppGuideABS
|
注意版本控制
|
10
|
分享
|
医生版:PTShareSheet
患者版:YMSharedManager.h
|
|
11
|
sheet
|
HDFActionSheet
|
|
12
|
语音识别
|
科大讯飞 iFly
|
|
13
|
语音识别输入框
|
XunfeiTextView
|
|
14
|
录音 播放
|
HDFVoiceRecorder
|
十一、附录2(公共类目)
序号
|
功能
|
对应文件
|
备注
|
---|---|---|---|
1 |
NSArray相关扩展API |
NSArray+HDFArray | NSArray的排序/JSON互转/是否包含String |
2 |
NSData相关扩展API |
NSData+HDFData NSDate+Addition |
String和二进制互转+各种加密算法 |
3 | 日期相关扩展 | NSDate+HDFDate | 所有和年月日星期,闰年等有关的日期操作 |
4 | NSDictionary相关扩展API | NSDictionary+HDFDictionary | NSDictionary和JSON互转,安全获取键值对 |
5 | 文件相关扩展 | NSFileManager+HDFFileManager | 检验文件是否存在、是否超时、判断文件大小 |
6 | 可变数组相关扩展API | NSMutableArray+HDFMutableArray | 可返回成功/失败状态的交换,移除,插入 |
7 | 可变字典相关扩展API | NSMutableDictionary+HDFMutableDictionary | 带空值检测,可安全的添加对象,网络请求参数,安全存储 |
8 | 常用的基类扩展API | NSObject+HDFObject |
1、获取类名 2、获取文本宽高 3、角度弧度互转 4、随机数 5、对象JSON互转 9、打开应用在App Store上的链接 10、移除所有值为null的key-value |
9 | NSString相关扩展API | NSString+HDFString | 1、加密 2、断言 3、URL包装 4、过滤HTML标签 5、前缀后缀 6、获取文件路径 7、获取沙盒路径 8、判断是否包含字符串 9、匹配非表情符号的正则表达式 |
10 | 定时器相关扩展 | NSTimer+HDFTimer | 1、block版本定时器(防内存泄露) 2、启动,暂停 |
11 | 偏好设置扩展 | NSUserDefaults+HDFUserDefaults | 更加安全的方式读写UserDefaults |
12 | ActionSheet扩展 | UIActionSheet+HDFActionSheet | block回调方式 |
13 | 弹窗扩展 | UIAlertView+HDFAlertView | 1、block回调方式 2、所有样式的弹窗构造方法 |
14 |
UIColor相关扩展API |
UIColor+HDFHYBColor | 1、根据颜色生成图片 2、根据同一颜色生成不同透明度颜色 |
15 | UIControl扩展API | UIControl+HDFControl | 按钮各种点击状态回调,值改变(segment)回调 |
16 | 设备相关扩展API | UIDevice+HDFDevice | 获取所有设备相关属性 |
17 | 手势相关扩展API | UIGestureRecognizer+HDFGesture | 手势扩展API,block方式回调 |
18 |
快速设置UILabel的attributedText属性的扩展API |
UILabel+HYBAttributedCategory |
快速设置UILabel的attributedText属性的API |
19 | 导航条相关扩展API | UINavigationBar+HDFNavigationBarTransluent |
快速设置导航条的各种属性 |
20 | UITextView相关扩展API | UITextView+HDFTextView | 设置UITextView的占位提示语 |
21 | UIView相关扩展API |
UIView+HDFView UIView+Frame |
快速获取UIView宽高位置等布局属性 |
22 | 各种系统控件的简洁创建配置 | UIKitMaker | 各种系统控件的简洁创建配置 |
23 | 计算文字高度 |
NSString+ParagrapshStyleSize NSString+ParagraphSzie |
使用attribute方式计算文字高度 |
24 |
快速由xib文件创建UITableViewCell |
UITableViewCell+InitCell | 只适用于xib画出来的cell |
25 | 富文本 | NSMutableAttributedString+LBAttributeString | Attribute方式制作富文本 |
26 | UITableViewController扩展 | UIViewController+MessageRightBarButtonItem | 带右上角未读标志的控制器 |
27 | UITabbarItem扩展 | UITabBarItem+CustomBadge | 带未读标志的tabbarItem |
28 | UIImage扩展1 | UIImage+Extend | 1、获得带颜色图片 2、缩放图片 |
29 | UIImage扩展2 | UIImage+Blur | 1、图片模糊效果 2、屏幕快照 |
30 | 计算文字size和高度 | NSString+sizeWithFont | 计算文字size和高度(boundingRectWithSize方式) |
31 | 快速创建图片button | UIButton+image | 快速创建图片button |
十二、附录3(公共控件)
序号
|
功能
|
对应文件
|
备注
|
---|---|---|---|
1 | 空数据页面 | PTEmptyDataView | |
2 | 定位功能 | PTLocation | |
3 | 底部弹出菜单 | HDFActionSheet | |
4 | 循环滚动banner | SDCycleScrollView | |
5 | 横向滚动视图 | HorizonTableView | |
6 | 进度条 | HUDProgressView | |
7 | 自定义tabbar(不可滚动) | HDFPageIndexView | |
8 | 自定义tabbar(可滚动) | PTTabbar | |
9 | 上下级联段选标签+控制器(类似网易新闻) | PTTabbarControllerFacade(PTTabbar+PTTabbarController) | |
10 | 本地草稿 | ANKeyValue |