前言
最近突发奇想,想到之前公司培训的代码规范,所以简单的整理一下,希望能对大家有所帮助。
正文
部分一
1. 命名
一定要含义明确,避免产生歧义,避免不必要的缩写,一般规则为类名,接口名(Protocol)均以大写字母开头,多单词组合时,后面的单词首写字母大写。
成员变量以小写字母开头,多单词组合时,后面的单词首写字母大写。
成员变量使用名词(表示某个属性,如title,bookid等)或形容词(表示状态,如enabled,hidden等)
函数、方法使用动宾格式(表示一个功能,如getTitle,parseXmlData,calculateScore等)。按照apple的惯例,私有成员变量以下划线(_)开头。
另外,像 onPlayOrCollectOrClock 这样的函数名以后应该被严格禁止!!!一种解决方案是:
打包在一个函数中处理,根据类型匹配区分操作。
2. 内存管理
1)注意delegate用weak类型
2)用到block的地方一定要注意block块里边的对象是否出现循环引用的问题(一般根据dealloc是否正常执行可以判断)
3. 关于魔数
- 原则上禁止在代码中硬编码(hard coding)使用魔数(magic number)。比如下面的代码应该被禁止
if(booktype == 1) {
} else if(booktype == 2) {
} else if(booktype == 3) {
}
这种情况应该在某个集中的位置或通用头文件中定义一组宏或枚举值来表示不同的类型,如
typedef NS_ENUM(NSInteger, BookTypeEnum) {
BookType_Computer,
BookType_Education,
BookType_Comic
};
另外, 不仅仅硬编码的数字被称为魔数;硬编码的常量字符串也是魔数!如果两个以上(含)地方使用到同一个魔数(比如:if([nssting isEqualToString:@"methodType"])),就要在一个统一的地方以合适的名称定义一个常量或宏来表示。
4. 调试/测试
1)要善于NSAssert、assert等工具(在debug模式有效)。合理安排断言,也是对自身对业务逻辑理解程度的一个检验。(使用方式:NSAssert([curList count] > 0, @"Empty catalog list got!");//第一个参数如果不符合条件(默认应该符合),则输出第二个参数(一般为异常情况))
2) 决不要仅满足于完成代码。完成一个功能要自己完整走一下流程,在第一时间消除不必要的bug。
5. 代码管理和版本控制
1)对于未确定处理逻辑或暂时的测试代码。统一加上在上一行加上注释“///TO DO:<有待确定的内容>“,提交代码时全project搜索”TO DO:"确认。
2) 提交代码时,在commit确认框中,养成输入说明信息的习惯。说明内容要明确表述所做修改。
3) 代码要及时上传。
6.关于基类的使用
- 有很多功能类似的类可以抽取出共同部分,继承一个自定义基类,基类里抽象出共同的属性和函数方法,在子类里统一调用基类的属性和函数,需要强调的是调用基类的方法时,注意子类回调。
7.关于子/父视图的处理
- 学习和习惯用面向对象和封装的思维方式来思考问题,不管是代码加载界面还是使用xib文件,界面的控件一般分上中下三部分作区块化处理,分别用一个底view(superview)来承载,当系统的自动调整尺寸,或者删除某一部分视图时,只操作底view即可,避免到处手动对一堆子view调整、删除。
8.关于代码结构的组织。
随着代码量的增多,代码结构的合理组织显得尤为重要。
1)建议将各个函数按功能划分区块,并用“#pragma mark -”隔开,这样可以借助XCode方便的定位代码。一般格式如下:
#pragma mark -
#pragma mark 功能区块的详细描述
2)对于category,函数的声明和定义位置一定要匹配!消除不必要的warning。
例如,在“@interface MyClass(MyExtend)"中声明的方法,一定要在“@implementation MyClass(MyExtend)”中定义,而不是在“@ implementation MyClass”中。
9.关于项目文件的组织结构。
- 为了方便团队协作,工程中引用的库和外部文件,一律使用相对路径,并且本地的目录结构务必保持与SVN(CVS)服务器上的目录结构一致。
部分二
1. NSFileManager在多线程中的使用。
- 根据apple docs的建议,在多线程中,主线程以外的其它线程不要直接使用
[NSFileManager defaultManager]
而是alloc一个新的实例:
NSFileManager *fileMan = [[NSFileManager alloc] init];
//other code
[fileMan release];
2. 图片资源格式问题。
- 根据以往经验,有些同学或者UI部门提供的PNG图片其实是伪装的(直接将jpg图像后缀改为png),此种情况下,可能会导致在build device版本时无法成功copy到app target中,造成显示不出来。这一个一定要注意!
3、需求文档、效果图和设计文档。
需求文档一定要认真阅读,确保准确理解需求。
明白区分“技术实现”和“产品实现”的区别;对开发进度评估不要故意延长时间,也要注意不要盲目乐观。
产品开发启动的前提是需求文档、设计文档和效果图全部提供,缺一不可,否则原则上拒绝给出具体的开发时间表。
开发过程中要严格按照效果图实现,有问题的及时找产品经理和UE设计人员沟通。
4.关于静态库(static library)。
1)静态库中的对SDK Class的category不被link到app的问题,在 Other Link Flags中添加“ -ObjC”。
2)静态库中对自定义Class的category不被link到app的问题,在Other Link Flags中添加“ -all_load”。
部分三 工程目录结构
主目录:放的是AppDelegate和一些系统常量及系统配置文件;
Base:一些基本父类,包括父ViewController和一些公用顶层自定义父类,其他模块的类一般都继承自这里的一些类;
Controller:系统控制层,放置ViewController,均继承于Group Base中的 BaseViewController或BaseTableViewController;
View:系统中视图层,由于我比较喜欢通过代码实现界面,所以这里放的都是继承于UIView的视图,我将视图从ViewController中分离出来全部放在这里,这样能保持ViewController的精简;
Model:系统中的实体,通过类来描述系统中的一些角色和业务,同时包含对应这些角色和业务的处理逻辑;
Network:网络请求,每个请求以API为前缀命名,对应上相应的Model
Manager:管理类,如数据缓存、推送设置、分享类、定位类等(常将第三方类库整合成自己的api来使用)
Utils:系统工具类(AppUtils),主要放置一些系统常用工具类;
Categories:类别,对现有系统类和自定义类的扩展;
Libs:用到的第三方类库
Resource:资源库,包括图片,plist文件等;
保持ViewController简单
1、View视图与ViewController分离
2、业务逻辑与ViewController分离
部分四 实践
- 不要使用尤达表达式
//尤达是星球大战中的jidi武师,他说话的语法都是很奇怪的倒装句,这里泛指这样的代码
if ([@8 isEqual:a]) // 错误
if ([a isEqual:@8]) // 正确
- 不要和YES,NO,Nil这些值做比较
if (great == YES) //错误
if (great) //正确
- 不过过度使用if嵌套,有时可以用return减少if嵌套
- (void)someMethod {
if (![someOther boolValue]) {
return;
}
//Do something important
}
复杂的表达式应该拆分多个语义更明确的字句
三元运算符 ? 应该只用在它能让代码更加清楚的地方
注意stong,weak,copy,assign的正确使用
尽量避免使用c的基本类型 ,例如:
int -> NSInteger
unsigned -> NSUInteger
float -> CGFloat
动画时间 -> NSTimeInterval
enum -> NS_ENUM
工厂方法或类构造器方法,返回类型需要使用instancetype而不要用id类型,方法中需要使用 [[xxx class]alloc] init]方法构造对象而不要用[[XXXClass alloc]init]
永远不要在init dealloc方法中使用 getter,setter,.属性等,你应当直接访问实例变量
类的构成方法(init方法)需要遵守Designated Secondary原则。这点在swift中被强制规定init和convenience构造方法。
designated 初始化方法一般提供所有的参数,有且只有一个,而secondary 初始化方法是一个或多个,并且提供一个或者更多的默认参数来调用 designated初始化方法定义你的 designated initializer,确保调用了直接超类的 designated initializer。
重载直接超类的 designated initializer。调用你的新的 designated initializer。
为新的 designated initializer 写文档。
永远不从 designated initializer 里面调用一个 secondary initializer
注意初始化模式的合理使用(类簇,单例)
合理使用懒加载Lazy loading
当实例化一个对象需要耗费很多资源,或者配置一次就要调用很多配置相关的方法而你又不想弄乱这些方法时,我们需要重写 getter 方法以延迟实例化要重写类的相等性时,需要同时实现isEqual和hash方法
常量推荐静态常量的声明,如果要暴露给外部,使用extern关键词,例如:
extern NSString *const ZOCCacheControllerDidClearCacheNotification;
static NSString * const ZOCCacheControllerDidClearCacheNotification = @"ZOCCacheControllerDidClearCacheNotification";
- 定义你自己的 NSNotification 的时候你应该把你的通知的名字定义为一个字符串常量
// Foo.h
extern NSString * const ZOCFooDidBecomeBarNotification
// Foo.m
NSString * const ZOCFooDidBecomeBarNotification = @"ZOCFooDidBecomeBarNotification";
- objc中可以合理使用代码块的特性,使局部变量更清晰
//代码块如果在闭合的圆括号内的话,会返回最后语句的值
NSURL *url = ({
NSString *urlString = [NSString stringWithFormat:@"%@/%@", baseURLString, endpoint];
[NSURL URLWithString:urlString];
});
合理使用Pragma Mark区分:不同功能组的方法,protocols 的实现,对父类方法的重写,View 的生命周期,自定义访问器,
方法注释最好用/** 开头,换行后第一句写方法的一句话描述,空一行后在写余下的描述, 还可以使用 | 来引用注释中的变量名及符号名而不是使用引号,例如 // Sometimes we need |count| to be less than zero.
函数定义中的block参数应该尽量放到最后一个参数,把需要提供的数据和错误信息整合到一个单独 block 中,比分别提供成功和失败的 block 要好
- (void)downloadObjectsAtPath:(NSString *)path
completion:(void(^)(NSArray *objects, NSError *error))completion;
函数定义中的error参数,尽可能方法函数的最后一个函数,并且遵循,数据为空,那么error不为nil,和error不为空,数据必须为空
block合理的使用self,weakself,strongSelf
一般来说:如果block不是属性则使用self,是属性但block中调用单个self的方法时用weakSelf,多个方法用strongSelfblock如果一行可以写完块,则没必要换行,块内的代码须按 4 空格缩进,果块太长,比如超过 20 行,建议把它定义成一个局部变量,然后再使用该变量,
不要使用new方法创建对象
结束语
到这里就结束了,如若不懂的话可以👇留言,也可以加入群讨论
喜欢的话 记得关注、收藏、点赞哟