iOS代码编程规范 根据项目经验汇总

栏目: IOS · 发布时间: 7年前

内容简介:带出几十位从零开始学iOS的实习生或试用期的开发人员后,觉得真的是千人千面,每个人写的代码都风格迥异,如果没有一个文档规范,每次都和新人进行口头的说教,大概自己是不用敲代码了,所以吃了亏了就开始编写iOS的编程规范。由于本人在写iOS代码前一直是C语言的开发,所以很多规范都受C语言的影响。与大家分享下我总结的编程规范,有不合适的请大家指出(软件的生命周期贯穿产品的开发,测试,生产,用户使用,版本升级和后期维护等过程,只有易读,易维护的软件代码才具有生命力。

带出几十位从零开始学iOS的实习生或试用期的开发人员后,觉得真的是千人千面,每个人写的代码都风格迥异,如果没有一个文档规范,每次都和新人进行口头的说教,大概自己是不用敲代码了,所以吃了亏了就开始编写iOS的编程规范。由于本人在写iOS代码前一直是 C语言 的开发,所以很多规范都受C语言的影响。

iOS代码编程规范 根据项目经验汇总

与大家分享下我总结的编程规范,有不合适的请大家指出( 最好能举例说明为何不好,并给一个好的推荐 )^_^

1. 指导原则

(1)首先是为人编写程序,其次才是计算机。

软件的生命周期贯穿产品的开发,测试,生产,用户使用,版本升级和后期维护等过程,只有易读,易维护的软件代码才具有生命力。

(2)保持代码的简明清晰,避免过分的编程技巧。

简单是最美。不要过分追求技巧,否则会降低程序的可读性。

(3)编程时首先达到正确性,其次考虑效率。

编程首先考虑的是满足正确性,健壮性,可维护性,可移植性等质量因素。

(4)编写代码时需要考虑到代码的可测试性。

不可以测试的代码是无法保障质量的。实现设计功能的同时,要提供可以测试、验证的方法。

(5)函数(方法)是为一特定功能而编写,不是万能 工具 箱。

方法是一个处理单元,是由特定功能的,所以应该很好地规划方法,不能是所有东西都放在一个方法里实现。

(6)鼓励多注释。

当代码有改动时,注释一定要修改,并且不要添加多余或重复的注释。

2. 布局

程序布局的目的是现实出程序良好的逻辑结构,提高程序的准确性、连续性、可读性、可维护性。统一的程序布局和编程风格,有助于提高整个项目的开发质量,提高开发效率,降低开发成本。

遵循统一的布局顺序书写头文件和实现文件:

文件头注释 #import 文件内部使用的宏 常量定义 文件内部使用的数据类型 全局变量 本地变量 类定义/实现

(1)If、else、else if、for、while、do等语句自占一行,执行语句不得紧跟其后,不论执行语句有多少都要加{}。

“{”前面添加一个空格,紧跟语句后。方法(函数)时,“{”另起一行并独        占一行书写。

if ( numberA > numberB ) {
    return numberA;
}
复制代码

(2)定义指针类型的变量,“*****” 应该放在变量前。

NSString *str1 = @"Hello World!";
复制代码

(3)源代码中关系较为紧密的代码应尽可能相邻。

CGFloat fWidth;
CGFloat fLength;
CGFloat fHeight;
复制代码

(4)禁止使用TAB键,必须使用空格进行缩进,缩进为4个空格。

在Xcode->Preferences->Text Editing->Indentation->Prefer indent using中,将值设置为Spaces。

(5)程序的分界符“{”和“}”在if、else、else if、for、while、do等语句时,“{”前添加空格紧跟语句后。在方法(函数)应独占一行并且位于同一列,同时与引用他们的语句对齐。“{}”之内的代码块使用缩进规则对齐。

- (void)dealloc
{
    // Do Something
}

if (isUpdated) {
        // Do Something
}
复制代码

(6)相关的赋值语句等号对齐。

promotionsEntity.promotionImageStr   = activityItemDict[@"promotion_image"];
promotionsEntity.promotionIdNum      = activityItemDict[@"promotion_id"];
promotionsEntity.promotionNameStr    = activityItemDict[@"promotion_name"];
promotionsEntity.promotionColorStr   = activityItemDict[@"promotion_color"];
复制代码

(7)在switch语句中,每一个case分支和default要用“{}”括起来,“{}”中的内容需要缩进。

(8)函数(方法)块之间使用一个空行分隔。

(9)一元操作符如!、~、++、--、*、&、和[]、.、->、前后不加空格。

!bValue
~iValue
++iCount
*strSource
&fSum
复制代码

(10)多元运算符和他们的操作数之间至少需要一个空格。

fWidth = 5 + 5;
fLength = fWidth * 2;
fHeight = fWidth + fLength;
复制代码

(11)关键字之后要留空格。“(”后和“)”前 不添加空格。

if、for、while等关键字之后应留一个空格再跟左括号”(”。

if (0 == fWidth)
复制代码

(12)方法名与形参不能留空格,返回类型与方法标识符有一个空格。

方法名后紧跟”:”,然后紧跟形参,返回类型”(”与”-”之间有一个空格。

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
复制代码

(13)注释符与注释内容之间要用一个空格进行分割。

/* 设置Frame的值 */
// TO DO
复制代码

(14)长表达式(超过80列)要在低优先级操作符处拆分成新行,操作符放在新行之首(以便突出操作符)。拆分出的新行要进行适当地缩进,使排版整齐。

if ( 0 == fWidth
    || 0 < fLength )
复制代码

(15)函数(方法)声明时,类型与名称不允许分行书写。

- (void)didReceiveMemoryWarning
复制代码

(16)类中功能模块以#pragma mark – 分割,上空两行,下空一行

#pragma mark – UITextFieldDelegate
复制代码

(17)方法实现时,参数过长则每个参数用一行,以冒号对齐。

- (void)doSomethingWith:(NSString *)theFoo
                   rect:(CGRect)theRect
               interval:(CGFloat)theInterval
{
复制代码

如果方法名比参数名短,则每个参数占用一行,至少缩进4个字符,且为垂直对齐(非冒号对齐)。

- (void)short:(NSString *)theFoot
    longKeyword:(CGRect)theRect
    evenLongerkeyword:(float)theInterval
{
}
复制代码

(18)方法调用沿用声明方法的习惯。

在同一行

[self doSomethingWith:@"test" rect:self.view.frame interval:1.0f];
复制代码

或 冒号对齐

[self doSomethingWith:@"test"
                 rect:self.view.frame
             interval:1.0f];
复制代码

(19)@public 和 @private 使用单独一行,且缩进1个字符

(20)Protocals声明中类型标识符、代理名称、尖括号间不留空格。

id<MyProtocalDelegate> delegate;
复制代码

在类声明中包含多个protocal,每个protocal占用一行,缩进2个字符。

@interface CustomBackButtonViewController () <UITextFieldDelegate,
  MyProtocalDelegate,
  UITabBarControllerDelegate,
  UITabBarDelegate>
复制代码

也可以与第一个对齐,保持清晰易读

@interface ShopViewController () <UIGestureRecognizerDelegate,
                                  HXSClickEventDelegate,
                                  UITableViewDelegate,
                                  UITableViewDataSource>
复制代码

如果并非所有方法都是必须得,使用@optional标示。

(21)UIViewController.m的文件布局,不同变量和方法的先后顺序:

<1> #import “LocationCustomButton.h”

最开始是导入需要使用到的其他class头文件,头文件按类型分类

// controller
#import “TextSelectionViewController.h"
// model
#import "PayPasswordUpdateModel.h"
#import "WalletModel.h"
// views
#import "PayPasswordAlertView.h"
复制代码

<2> #define KEY_BANK_TAIL @“bank_tail"

添加宏定义,将在文件中需要使用到的常量,字符串等用宏定义。

<3> @property (nonatomic, strong) NSArray *bankCardList;

property的属性定义。不用将需要使用的methods声明,在iOS7后Methods已经不需要先声明再使用了。

<4> - (void)viewDidLoad

#pragma mark - Life Cycle 添加生命周期的方法

- (void)viewWillAppear:(BOOL)animated
 - (void)viewDidAppear:(BOOL)animated
- (void)viewWillDisappear:(BOOL)animated
-(void)viewDidDisappear:(BOOL)animated
- (void)didReceiveMemoryWarning
- (void)dealloc
复制代码

<5> #pragma mark - override

重载父类的方法

<6> #pragma mark - Intial Methods

初始化的方法,如

- (void)initialNavigationBar
- (void)initialTableView
复制代码

<7> #pragma mark - Target Methods

点击事件或通知事件

<8> #pragma mark - UITextFieldDelegate

Delegate的事件,将所有的delegate放在同一个pragma下

<9> #pragma mark - Setter Getter Methods

所有的property使用懒加载,并将setter或getter放在底部,不影响业务代码的阅读。

<10> #pragma mark - private method

私有方法的代码尽量抽取创建公共class。

3. 注释

(1)多行注释采用”/* … */”, 单行注释采用 “// …”.

(2)一般情况下,源程序有效注释量必须在30%以上。

注释语言不宜太多也不能太少,注释语言必须准确、易懂、简洁。

(3)注释应与其描述的代码相近,对代码的注释应放在其上方或右方相邻位置,不可放在下方,如放于上方则需要与其上面的代码用空行隔开。

(4)注释与所描述内容进行同样地缩排。

(5)文件最前面的注释,是保留了工程自动生成的注释,但是需要进行如下修改:文件作者改为个人的名字,公司名为Insigma Hengtian software Ltd. 如:

//
//  ViewController.m
//  test
//
//  Created by ArthurWang on 14-5-7.
//  Copyright (c) 2014年 Insigma HengTian Software Ltd. All rights reserved.
//
复制代码

(6)类、协议、结构体注释。如:

/*
 @class HTServerDatamanager
 @abstract 异步连接服务器管理类
 @discussion 异步请求服务器,接收到响应后,通过回调把数据回传到对象
 */
复制代码

(7)成员方法、接口注释。如:

/*
 @method  initWithTarget:selector:
 @abstract  类初始化函数
 @discussion  本类使用时必须调用此函数进行初始化
 @param target 响应回调对象
 @param selector  回调对象的selector
 @result   类对象
 */
复制代码

4. 命名

(1)标识符要采用英文单词或其组合,便于记忆和阅读,切忌使用汉语拼音来命名。

标识符应当直观且可以拼读,可望文知意,英文单词一般不要太复杂,用词应当准确。

(2)严格禁止使用连续的下划线,下划线也不能出现在标识符头或结尾。(实例变量及特殊用法除外)

CGFloat variable__name;
NSString *variale___name;
复制代码

(3)程序中不要出现仅靠大小区分的相似的标识符。

NSString *contentOfView;
NSString *ContentOfView;
复制代码

(4)宏、常量名都要使用大写字母,用下划线‘_’分割单词。

#define URL_GAIN_QUOTE_LIST @"/v1/quote/list"
#define URL_UPDATE_QUOTE_LIST @"/v1/quote/update"
#define URL_LOGIN   @"/v1/user/login”
复制代码

(5)程序中局部变量不要与全局变量重名。

尽管局部变量和全局变量的作用域不同而不会发生语法错误,但容易使人误解。

(6)使用一致的前缀来区分变量的作用域。

g_  全局变量 s_  模块内静态变量

(7)方法名用小写字母开头的单词组合而成。

方法名力求清晰、明了、通过方法名就能够判断方法的主要功能。方法名中不同意义字段之间不要用下划线连接,而要把每个字段的首字母大写以示区分。

- (NSString *)descriptionWithLocale:(id)locale;
复制代码

(8)尽量避免名字中出现数字编号,如Value1, Vlaue2等,除非逻辑上的确需要编号。

(9)声明实例变量,都采用property。

不在类声明和实现的“{”与“}”之间声明。

@interface LumberjackViewController ()
{
    NSArray *dataSource;  // 错误:
}
复制代码

(10)类名(及其category 和protocal)的首字母大写,使用字母大写的形式分割单词。

5. 变量

变量、常量和数据类型是程序编写的基础,是直接关系到程序设计的成败。

(1)一个变量有且只有一个功能,尽量不要把一个变量用作多种用途。

一个变量只用来表示一个特定功能,不要把一个变量作多种用途。

(2)循环语句与判断语句中,不允许对其它变量进行计算与赋值。

错误: if ( 100 > (fWidth = 50 * fLength) )

(3)宏定义中如果包含表达式或变量,表达式和变量必须用小括号括起来。

#define MY_MIN(A, B)   ((A)>(B)?(B):(A))
复制代码

(4)宏名大写字母

(5)对于全局变量通过统一的函数访问。

(6)最好不要在语句块内声明局部变量。

(7)系统常用类作实例变量声明时加入后缀:

UIViewController: VC                      UIImage: Img
UIImageView:ImagView                   UIView:View
UILabel: Lbl                                    UIButton:Btn
UINavigationBar:Nbar                     UIToolbar:Tbar
UISearchBar:Sbar                            UITextField:TextField
UITextView:TextView                      NSArray:Array
NSMutableArray:Marray                  NSDictionary:Dict
NSMutableDictionary:Mdict             NSString:Str
NSMutableString:MStr                     NSSet:Set
NSMutableSet:Mset
复制代码

(8)属性声明严把权限,对不需要外部修改的属性使用readonly。

(9)NSString使用copy而非retain。

在ARC中NSString的使用Strong与Copy的效果一样。

(10)CFType 使用@dynamic,禁止使用@synthesize

(11)除非必须,使用nonatomic。

(12)定义NSArray和NSDictionary使用泛型,提高代码可读性和健壮性。

NSArray<NSString *> *testArr = [NSArray arrayWithObjects:@"Hello", @"world", nil];
NSDictionary<NSString *, NSNumber *> *dic = @{@"key":@(1), @"age":@(10)};
复制代码

在* 符号前面都添加一空格。

6. 表达式

表达式是语句的一部分,他们是不可分割的。

(1)一条语句只完成一个功能。

复杂的语句阅读起来,难于理解,并容易隐含错误。

(2)在表达式中使用括号,使表达式的运算顺序更清晰。

由于将运算符的优先级与结合律熟记是比较困难的,为了防止产生歧义并提高可读性,即使不加括号时运算顺序不会改变,也应当用括号确定表达式的操作顺序。

if ( (( 0 == iYear%4 ) && ( 0 != iYear%100 )) || ( 0 == iYear%400 ) )
复制代码

(3)避免表达式中的附加功能,不要编写太复杂的复合表达式。

错误:int iResult = iYear++-++iMonth+iDay++;

(4)不可将布尔变量和逻辑表达式直接与YES、NO或则1、0进行比较。

if (isSuccess)   //真
if (!isSuccess)  //假
复制代码

(5)在条件判断语句中,当整型变量与0比较时,不可模仿布尔变量的风格,应当将整型变量用“==”或“!=”直接与0比较。

if (0 == iYear)
if ( 0 != iMonth )
复制代码

(6)应当将指针变量用“==”或“!=”与nil比较。

指针变量的零值是“空”(即nil),nil的值与0相同,但是两者含义不同。

if ( nil == strName )
复制代码

(7)在switch语句中,每一个case分支必须使用break结尾,最后一个分支必须是default分支。

避免漏掉break语句造成程序错误,同时保持程序简洁。对于多个分支相同处理的情况可以共用一个break,但是要用注释加以说明。

(8)不可在for循环内修改循环变量,防止for循环失去控制。

(9)循环嵌套次数不大于3次。

(10)do while 语句和while语句仅使用一个条件。

(11)如果循环体内存在逻辑判断,并且循环次数很大,宜将逻辑判断移到循环体的外面。

(12)for语句的循环控制变量的取值采用“半开半闭区间”写法。

这样做更能适应数组的特点,数组的下标属于一个“半开半闭区间”。

int iMax[1000];
for (int i = 0; i < 1000; i++)
{
    NSLog(@"%d", iMax[i]);
}
复制代码

(13)将int值转换为BOOL时应特别小心。

(14)OC中,BOOL被定义为unsigned char,这意味着除了YES(1)和NO(0)外它还可以是其他值。禁止将int直接转换为BOOL。

(15)将整型值转换为BOOL的方法:使用三元运算符返回YES/NO,或使用&&,||。

(16)BOOL、_BOOL和bool之间的转换是安全的,但是BOOL和Boolean间的转换不是安全的,所以将Boolean堪称整型值。

(17)在OC中,只允许使用BOOL。

7. 函数

(1)方法不能为多个目的服务。

一个方法一个功能。

(2)在接口中应该尽量少使用外部定义的类型(减少耦合)。

(3)避免函数有太多的参数,参数个数尽量控制在5个以内。

如果参数的确比较多,不妨把这些参数定义成一个结构(或一个类)。

(4)对于有返回值的函数(方法),每一个分支都必须有返回值。

为了保证对被调用函数返回值的判断,有返回值的函数中都每一个退出点都需要有返回值。

(5)对输入参数的正确性和有效性进行检查。

很多程序错误和崩溃是由非法参数引起的。

(6)防止将函数(方法)的参数作为工作变量。

将函数的参数作为工作变量,有可能错误地改变参数内容。对必须改变的参数,最好先用局部变量代之,最后再将该局部变量的内容赋给该参数。

(7)函数(方法)体的规模不能太大,尽量控制在200行之内。

冗长的函数不利于调试,可读性差。

(8)禁止直接调用NSObject的类方法+new,也不要在子类中重载它。使用alloc和init方法。

(9)创建对象时尽量使用autorelease,创建临时对象时,尽量同时在同一行中autorelease掉,而非使用单独的release语句。

(10)Dealloc的顺序要与变量声明的顺序相同。

这样有利于review代码。 如果dealloc中调用其他方法来release变量,将被release的变量以注释的形式标注清楚。 先release自身成员变量,再调用父类dealloc方法。

8. 头文件

(1)申明成员类,应该引用该类申明,而不是包含该类的头文件。

@class MyViewController;
@interface ViewController : UIViewController
{
    MyViewController *_myViewController;
}
复制代码

(2)共同的接口、结构体、常量和数据类型要定义在同一个头文件里。

(3)使用#import引入OC和OC++头文件,使用#include引入c和c++头文件。

9. 可靠性

为保证代码的可靠性,编程时请遵循如下基本原则,优先级递减: 正确性 稳定性 可测试性 规范/可读性 全局效率 局部效率 个人表达方式/个人方便性

(1)防止内存操作越界

内存操作主要是指对数组、指针、内存地址等得操作,内存操作越界是软件系统主要错误之一,后果往往非常严重,引起崩溃。

(2)当变量释放后,需要将变量置为nil。

避免因为野指针引起的程序崩溃。

(3)变量在使用前应初始化,防止未初始化的变量被引用。

引用未初始化的变量,会引起程序的崩溃。

(4)指针类型变量必须初始化为nil。

(5)指针不要进行复杂的逻辑或算术操作。

通过复杂的逻辑或算术操作后,指针的位置就很难确定。

(6)减少指针和数据类型的强制类型转化。

强制类型转化如果类型强转错误会引起崩溃。

(7)对变量进行赋值时,必须对其值进行合法性检查,防止越界等现象发生。

(8)非初始化方法中的alloc操作之前必须要nil判断。

(9)在编写派生类的赋值时,主要不要忘记对基类的成员变量重新赋值。

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
}
复制代码

(10)私有方法应该在实现文件中申明。

@interface ViewController ()
- (void)login;
@end
复制代码

10. 断言

断言是对某种假设条件进行检查,可以快速发现并定位软件文件,同时对系统错误进行自动报警。

(1)整个软件系统应该采用统一的断言。

assert(str);

(2)正式软件产品中应把断言及其它的调测代码去掉。

加快软件运行速度。

11. 其他

(1) 避免过多直接使用立即数。

应该都使用宏定义,采用立即数不容易理解含义并容易出错。

(2) 枚举第一个成员要赋初始值。

(3) addObject之前要非空判断。

(4) release版本代码去掉NSLog打印,除了保留异常分支的NSLog。

(5) 禁止在代码中直接写死字符串资源,必须要用字符串ID替代。

应该考虑多语言国际化,尽量使用NSLocalizedStringFromTable实现对字符串ID的引用。

(6) 对于框架设计,逻辑层尽量与UI层分离,降低耦合度。

(7) delegate对象使用assign,禁止使用retain。

因为retain会引起导致循环索引导致内存泄露,并且对类型的内存泄露无法被Instrument发现,极难调试。

(8) Controller独立于View和Controller。

不要在View相关的类中添加过多的业务逻辑代码,这让代码的可重用性很差。Controller负责业务逻辑代码,且Controller的代码与View尽量无关。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Advanced Web Metrics with Google Analytics

Advanced Web Metrics with Google Analytics

Brian Clifton / Sybex / 2008 / USD 39.99

Are you getting the most out of your website? Google insider and web metrics expert Brian Clifton reveals the information you need to get a true picture of your site's impact and stay competitive usin......一起来看看 《Advanced Web Metrics with Google Analytics》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

在线进制转换器
在线进制转换器

各进制数互转换器

MD5 加密
MD5 加密

MD5 加密工具