编码规范对于程序员而言尤为重要,有以下几个原因:
Ø 一个软件的生命周期中,80%的花费在于维护
Ø 几乎没有任何一个软件,在其整个生命周期中,均由最初的开发人员来维护
Ø 编码规范可以改善软件的可读性,可以让程序员尽快而彻底地理解新的代码
Ø 如果你将源码作为产品发布,就需要确任它是否被很好的打包并且清晰无误,一如你已构建的其它任何产品
建立IOS语言编程规范的主要目的是:
1) 统一编程风格
2) 提高代码的可阅读性
3) 减少错误产生
4) 减少性能漏洞
5) 提高代码可靠性
6) 减少错误的编码设计
7) 作为代码检查的依据
8) 建立可维护的IOS语言编程规范
标识符的命名要清晰、明了,有明确含义,同时使用完整的单词或大家基本可以理解的缩写,避免使人产生误解。
缩写说明:较短的单词可通过去掉“元音”形成缩写;较长的单词可取单词的头几个字母形成缩写;一些单词有大家公认的缩写。
示例:如下单词的缩写能够被大家基本认可。
temp可缩写为 tmp ;
flag可缩写为 flg ;
statistic可缩写为 stat ;
increment可缩写为 inc ;
message可缩写为 msg ;
命名中若使用特殊约定或缩写,则要有注释说明。
注释说明:应该在源文件的开始之处,对文件中所使用的缩写或约定,特别是特殊的缩写,进行必要的注释说明。
对于变量命名,禁止取单个字符(如i、j、k...),建议除了要有具体含义外,还能表明其变量类型、数据类型等,但i、j、k作局部循环变量是允许的。
变量说明:变量,尤其是局部变量,如果用单个字符表示,很容易敲错(如i写成j),而编译时又检查不出来,有可能为了这个小小的错误而花费大量的查错时间。
命名解释定义如下:
类别 | 解释 | 符号 | 示例 |
生命周期 | 局部变量 | l_ | l_iCount;iMax; |
全局变量 | g_ | g_iUserCount; | |
成员变量(C/C++) | m_ | m_UserName; | |
成员变量(ObjC) | _ | _userName; | |
数据类型 | typedef自定义类型 | t | TmyViewType; |
NSInteger/int | i/n | iCount; | |
NSUInteger/unsigned | u | uScore; | |
CGFloat/float double | f | fProgress; | |
char | c | cTitle; | |
bool | b | bIsEmpty; | |
NSString | sz | nameString; | |
pointer(C/C++) | p |
| |
标示符类型 | 变量 | v |
|
常量 | k | kKeyName;kTagHomeView; |
命名规范必须与所使用的系统风格保持一致,并在同一项目中统一,不要使用大小写与下划线混排的方式;用作特殊标识如标识成员变量或全局变量的m_/_和g_,其后加上大小写混排的方式是允许的。
示例: Add_User不允许,add_user、AddUser、m_AddUser、_addUser允许。
除了编译开关/头文件等特殊应用,应避免使用_EXAMPLE_TEST_之类以下划线开始和结尾的定义。
混合编程中C/C++部分沿用C/C++编程规范中对应部分。
C++类名:采用驼峰式命名法以大写字母C开头作为标识,例如classCMyClass。
Objective-C类命名:采用ObjC模式,子类的结尾标识与父类保持一致(Object后缀可省略)(需要时候可加入前缀,前缀标识属于某模块或者框架),例如
/*! 类的说明
*/
@interface MyClass:NSObject {
// 声明成员标量
}
// 静态方法声明
// 如果需要声明初始化方法、在此声明初始化方法
// 声明类的属性
// 声明类的实例方法
- (NSString *)name;
@end
为了区别混合编程中C/C++方法和ObjC方法的区别,C/C++方法采用以大写字母开头的驼峰式命名法,ObjC方法采用小写字母开头的驼峰式命名法。
方法名称:必须明确表达出该方法的用途,驼峰名中第一段须表示出该方法的主要动作或者宿主对象,例如可采用这种方式:btnPressed,使用第一段btn用于表示这是一个按钮方法。
get和set在ObjC中有明确含义,在非自定义的获取器和设置器方法中,避免使用get和set开头的方法名称,以免造成和系统生成的方法冲突而歪曲了代码的意图。
方法命名应体现出该方法的内存管理信息,如果方法内部创建了对象并没有释放,需要由调用者释放,则方法名以C(Create)结尾,表示其内部创建了未释放的对象。
ObjC方法含有多个参数时,应采用分段命名方式,每个分段采用以小写字母开头的驼峰式命名法明确的表示出该参数的含义,形参可采用一个小写单词来表示。
.h头文件
/* 省略对头文件的说明
*/
#import <UIKit/UIKit.h>
// SetterView显示时的通知
#define kNotificationSetterShow @"kNotificationSetterShow"
// Dictionary中的namekey标识
#define kKeyNameString @"name"
// Dictionary中的sexkey标识
#define kKeySexString @"sex"
// Dictionary中的agekey标识
#define kKeyAgeNumber @"age"
// FollowView的tag标识
#define kTagFollowView 1001
// SetterView的tag标识
#define kTagSetterView 1002
// 类型定义
typedef enum {
kTestClassAdmin,
kTestClassCustom,
}TestClassType;
// ObjC类定义
// (参考)@interfaceTestClassViewController : UIViewController
// (参考)@interfaceTestClass : NSObject
//
@interface TestClassView : UIView
{
// 类成员属性
NSInteger _iValue; //整型
NSString *_nameString; // 字符串型
NSArray *_titles; //数组对象
NSDictionary *_valueDictionary; // 字典对象
// 类的UI成员属性
UILabel *_nameLabel;
UIButton *_nameButton;
IBOutlet UIButton *_pictureButton;
IBOutlet UIView *_setterView;
}
// 函数说明
- (id)initWithType:(TestClassType)type;
@property (nonatomic, assign) BOOL isHidden;
// ObjC方法 函数说明
- (void)load;
// C/C++方法 函数说明
float GetGlobal();
@end
.m文件
/* 省略对文件的说明
*/
// 全局变量命名
CGFloat g_fHeight = 480;
@implementation TestClassView
{
// 私有类成员属性
NSInteger _iValue; //整型
NSString *_nameString; // 字符串型
NSArray *_titles; // 数组对象
NSDictionary *_valueDictionary; // 字典对象
// 私有类的UI成员属性
UILabel *_nameLabel;
UIButton *_nameButton;
IBOutlet UIButton *_pictureButton;
IBOutlet UIView *_setterView;
}
// ObjC方法 函数说明
- (void)load
{
}
// C/C++方法 函数说明
float GetGlobal()
{
return g_fHeight;
}
- (IBOutlet)showSetterView:(id)sender
{
}
@end
模板如下:
//
// 文件名称
// 项目名称
//
// Created by 创建人on 13-6-4.
// Copyright (c) 2013年fengguixian. All rights reserved.
//
/*
文件描述一项描述本文件的内容、功能、内部各部分之间的关系及本文件与其它文件关系等。
主要函数及其功能、修改日志等。可以适当选择你要包含的信息。
*/
(推荐插件https://github.com/onevcat/VVDocumenter-Xcode)
模板如下:
/*!
* 类的功能描述、备注、修改记录、Review信息
*/
模板如下:
/*!
* 函数描述
*
* @param 参数名 描述信息
* @param 参数名 描述信息
*
* @return返回值描述信息
*/
新增代码记录:
//BEGIN:Added by 姓名 at 时间 on 版本号(或者 for原因)
//END: Added by 姓名 at 时间 on 版本号(或者 for原因)
修改代码记录:
//BEGIN:Modified by 姓名 at 时间 on 版本号(或者 for原因)
//END: Modified by 姓名 at 时间 on 版本号(或者 for原因)
删除代码记录:
//BEGIN:Deleted by 姓名 at 时间 on 版本号(或者 for原因)
//END : Deleted by 姓名 at 时间 on 版本号(或者 for原因)
空一行后添加修订记录、修改内容、Review记录三行。
修改记录填写姓名、修改时间和修改版本号;
修改内容填写此次修改的内容,可使用1,2,3详细列出;
Review记录只添加这个标题,修改人不写内容,Review人填写。
示例:
修改记录 :s00113738 2012-4-21 MobileKalaokV210Release20140212
修改内容 : 修改函数addMission()
Review记录:
边写代码边注释,修改代码同时修改相应的注释,以保证注释与代码的一致性。不再有用的注释要删除。
注释的内容要清楚、明了,含义准确,防止注释二义性。
避免在注释中使用缩写,特别是非常用缩写。
尽量使用中文注释。
注释应与其描述的代码相近,对代码的注释应放在其上方或右方(对单条语句的注释)相邻位置,不可放在下面,如放于上方则需与其上面的代码用空行隔开。
对于所有有物理含义的变量、常量,如果其命名不是充分自注释的,在声明时都必须加以注释,说明其物理含义。变量、常量、宏的注释应放在其上方相邻位置或右方。
示例:
/*active statistic task number */
#defineMAX_ACT_TASK_NUMBER 1000
#define MAX_ACT_TASK_NUMBER 1000 /*active statistic task number */
数据结构声明(包括数组、结构、类、枚举等),如果其命名不是充分自注释的,必须加以注释。对数据结构的注释应放在其上方相邻位置,不可放在下面;对结构中的每个域的注释放在此域的右方。
示例:可按如下形式说明枚举/数据/联合结构。
/*sccp interface with sccp user primitive message name */
enumSCCP_USER_PRIMITIVE
{
N_UNITDATA_IND,/* sccp notify sccp user unit data come */
N_NOTICE_IND,/* sccp notify user the No.7 network can not */
/*transmission this message */
N_UNITDATA_REQ,/* sccp user's unit data transmission request*/
};
全局变量要有较详细的注释,包括对其功能、取值范围、哪些函数或过程存取它以及存取时注意事项等的说明。
示例:
/*The ErrorCode when SCCP translate */
/*Global Title failure, as follows */ // 变量作用、含义
/*0 - SUCCESS 1 - GT Table error*/
/*2 - GT error Others - no use */// 变量取值范围
/*only function SCCPTranslate() in */
/*this modual can modify it, and other */
/*module can visit it through call */
/*the function GetGTTransErrorCode() */ // 使用方法
BYTEg_GTTranErrorCode;
说明:可使程序排版整齐,并方便注释的阅读与理解。
示例:如下例子,排版不整齐,阅读稍感不方便。
voidexample_fun( void )
{
/*code one comments */
CodeBlock One
/*code two comments */
CodeBlock Two
}
应改为如下布局。
voidexample_fun( void )
{
/* code one comments */
CodeBlock One
/* code two comments */
CodeBlock Two
}
2.2.12 在代码的功能、意图层次上进行注释,提供有用、额外的信息
说明:注释的目的是解释代码的目的、功能和采用的方法,提供代码以外的信息,帮助读者理解代码,防止没必要的重复注释信息。
示例:如下注释意义不大。
/* if receive_flag is TRUE */
if (receive_flag)
而如下的注释则给出了额外有用的信息。
/* if mtp receive a message from links */
if (receive_flag)
2.2.13 在程序块的结束行右方加注释标记,以表明某程序块的结束
说明:当代码段较长,特别是多重嵌套时,这样做可以使代码更清晰,更便于阅读。
示例:参见如下例子。
If (...) {
// program code
while (index < MAX_INDEX)
{
// program code
} /* end of while (index <MAX_INDEX) */ // 指明该条while语句结束
} /* end of if (...)*/ // 指明是哪条if语句结束
2.3 内存规范 (MRC)
ManualReference Counting:人工引用计数,MRC内存管理的基本原则是“谁创建谁管理,创建和释放要配对”。
2.3.1 assign/retain/ copy属性的使用
assign: 在进行赋值时内存引用计数不增加; 用于基础类型: 如
NSInteger,NSUInteger,CGFloat, int,float,double,char等;
retain: 在进行赋值时会将入参retain一次,内存引用计数加1.在被赋值的变量使用完后,必须将其release掉.在对象需要保留时必须使用retain属性;
copy: 在进行赋值时进行,它会将入参拷贝一份.通常用在字符串的属性设置。
2.3.2 在alloc一个对象之后,使用完后一定要使用release将其释放
如果时alloc的一个临时变量,则在临时变量用完后立即释放;
如:
customView = [[UICustomAlertView alloc] initDoubleWithMsg: @"确定要停止播放么 ?"
delegate: nil];
[customView show];
[customView release];
customView = nil;
2.3.3 在子线程中,尽量避免使用 autorelease;如果必须要用到 autorelease,则需要在其方法的开始处初始化一个自动释放池对象并在方法返回前将自动释放池释放掉
2.3.4 在自定义的类中,如果存在具有 retain 属性的成员变量,必须在dealloc方法中将具有 retain 属性的成员变量 release 掉
2.3.5 在方法函数分支较多的情况下,如果函数中存在有new或者alloc的对象,则必须在函数返回(return)前将其delete或者release掉
2.3.6 输出参数为new 或者alloc 的对象时必须在使用完后delete 或者release掉
2.3.7 具有 IBOutlet 申明的成员变量必须在dealloc时将其release掉
2.3.8 具有 retain/copy 申明的属性必须在dealloc时将其置空( = nil)(适用于ARC)
2.4 内存规范 (ARC)
ARC(AutomaticReference Counting:自动引用计数), 苹果的xcode在4.5版本之后创建的工程都是ARC的,但是ARC只适合苹果自己的编译器,如果要开发跨平台或者跟c/c++混编的应用还是建议使用MRC。
2.5.1 代码的缩进应使用4个空格(SPACE)
if (...){
//your codes
}
else if (…) {
}
else {
//your codes
}
//
for(int i = 0; i < iCount; i++) {
//
}
Ø 关键字与其后的表达式之间要有空格。(如: if (expr) 或 for (expr))
Ø 单目操作符不应与它们的操作数分开。(如'!'和'^'等)
Ø 除','外,其它双目操作符应与它们的操作数用空格隔开。 (如 i = i +1; if (a > b))
Ø .h中协议 <>前面有一个空格。
Ø .h中成员声明时,类型与变量之间有至少1个空格。
Ø *号靠近变量,不靠近类型。
Ø @property后留1个空格,()里面,逗号紧跟前一变量,与后一变量之间留1 个空格。()外面,先留1个空格,再声明属性。(如@property (nonatomic, assign) BOOL isHidden; // )
Ø 方法的+,-后面与()之间留1个空格。
Ø 返回类型与*之间留1个空格,方法参数中返回类型与*之间留1个空格。
Ø 在多参数方法中,每个参数后面都有1个空格。
Ø 头文件包含(#import)与@class之间空1行。
Ø 如果需要声明protocol,空1行接着写。通常protocol写在@end后面,但是声明在@interface之前。
Ø 文件说明与头文件包含(#import)之间空1行。
Ø 头文件包含(#import)之间,如果需要分类区别,各类别之间空1行。
Ø @interface与@class/@protocol之间空2行。
Ø 头文件{}里面,空1行开始声明对象成员,如果需要分类区别,各类别之间空1行。
Ø 头文件{}外,空1行书写属性,如果需要分类区别,各类别之间空1行。
Ø 属性下面空1行开始写方法,如果需要分类区别,各类别之间空1行。
Ø 方法完成后,空1行@end。
Ø 文件说明与头文件包含(#import)之间空1行。
Ø 头文件包含(#import)之间,如果需要分类区别,各类别之间空1行。
Ø @implementation和@synthesize之间空1行, 如果需要分类区别,各类别之间空1行。
Ø 方法与方法之间空1行。
Ø 变量声明后需要空1行,如果需要分类区别,各类别之间空1行。
Ø 条件、循环,选择语句,整个语句结束,需要空1行。
Ø 各功能快之间空1行。
Ø #pragma mark 与方法之间空1行。
Ø 最后一个括弧之前不空行。
Ø 注释与代码之间不空行。
Ø 每行代码最多不得超过100个字。设置如下:Xcode=> Preferences => TextEditing => Page Guide at column /输入 100即可。
Ø 注意代码的对齐、美观。
Ø 循环或者判断最多内嵌三层。
Ø 一个函数的代码行数应该控制在50行以内。
Ø 避免出现两次以及两次以上的重复代码块。
Ø 项目中的 .h头文件尽量避免冗余信息。
Ø .h文件中尽量只声明对外部公开的成员变量,不对外公开的放到.m文件中。例如:.h文件
@interface ItemString : NSObject
{
}
@property (nonatomic) NSUInteger iVoteResult;//投票数
@property (copy, nonatomic) NSString *sItemID;//投票项号
@property (copy, nonatomic) NSString *sItemName;//投票项名字
@property (nonatomic) NSUInteger nIsSelected;//1:表示选中,0:表示未选中
@end
.m文件
@interface ItemString ()
@property(nonatomic) int friendCounts;//不对外公开的变量
@end
使用的时候可以直接:
_sItemID=
_friendCounts =