iOS代码规范

iOS软件代码规范

1.指导原则

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

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

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

说明:简单是最美。保持代码的简单化是软件工程化的基本要求。不要过分追求技巧,否则会降低程序的可读性。

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

说明:编程首先考虑的是满足正确性、健壮性、可维护性、可移植性等质量因素,最后才考虑程序的效率和资源占用。

1.4 编写代码时要考虑到代码的可测试性。

说明:不可以测试的代码是无法保障质量的,开发人员要牢记这一点来设计、编码。实现设计功能的同时,要提供可以测试、验证的方法。

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

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

1.6 鼓励多加注释。

1.7内存空间在哪分配在哪释放。

1.8 语言

说明:应该使用US英语。

应该:
UIColor *myColor = [UIColor whiteColor];   
不应该:
UIColor *myColour = [UIColor whiteColor];   

2. 布局

  • 程序布局的目的是显示出程序良好的逻辑结构,提高程序的准确性、连续性、可读性、可维护性。更重要的是,统一的程序布局和编程风格,有助于提高整个项目的开发质量,提高开发效率,降低开发成本。同时,对于普通程序员来说,养成良好的编程习惯有助于提高自己的编程水平,提高编程效率。因此,统一的、良好的程序布局和编程风格不仅仅是个人主观美学上的或是形式上的问题,而且会涉及到产品质量,涉及到个人编程能力的提高,必须引起大家重视。

2.1 文件布局

2.1.1遵循统一的布局顺序来书写头文件。
说明:以下内容如果某些节不需要,可以忽略。但是其它节要保持该次序。

头文件布局:
          文件头(参见“注释”一节)
          #import (依次为标准库头文件、非标准库头文件)
          全局宏
常量定义
          全局数据类型
          类定义
正例:
/********************************** 文件引用 *******************************/ 

/********************************** 类引用 ********************************/ 

/********************************** 宏定义 ********************************/

/*********************************** 常量 ********************************/

/********************************* 类型定义 *******************************/ 

/********************************** 类定义 ********************************/

/********************************* 文件引用 *******************************/ 

/********************************* 文件引用 *******************************/ 
2.1.2 遵循统一的布局顺序来书写实现文件。

说明:以下内容如果某些节不需要,可以忽略。但是其它节要保持该次序。

实现文件布局:
          文件头(参见“注释”一节)
          #import (依次为标准库头文件、非标准库头文件) 
          文件内部使用的宏
常量定义
          文件内部使用的数据类型
全局变量
本地变量(即静态全局变量)
          类的实现

正例:
/********************************** 文件引用 *******************************/ 

/********************************** 宏定义 ********************************/ 

/********************************** 常量 *********************************/ 

/********************************* 类型定义 ******************************/ 

/********************************* 全局变量 ******************************/ 

/********************************** 原型 ********************************/ 

/********************************* 类特性 *******************************/ 
@implementation ClassName
@synthesize variableName;
/********************************* 类的实现 *****************************/ 
2.1.3 包含标准库头文件用尖括号 < >,包含非标准库头文件用双引号 “ ”。
正例:
#import 
#import “heads.h”
2.1.4   代码组织
在函数分组和protocol/delegate实现中使用#pragma mark -来分类方法,要遵循以下一般结构:
//按钮以及其他控件Change事件

//ViewController 生命周期
#pragma mark - Life cycle
- (void)dealloc {}
- (instancetype)init {}
- (void)viewDidLoad {}
- (void)viewWillAppear:(BOOL)animated {}
- (void)didReceiveMemoryWarning {}

//通知事件
#pragma mark - Notificaion
- (void)keyboardWillAppear:(NSNotification *)aNotification {}
//手势方法
#pragma mark - Gesture
- (void)backgroundViewTapGesture:(UITapGestureRecognizer *)sender {}
//按钮以及其他控件Change事件
#pragma mark - Action
- (IBAction)registerButtonHandler:(id)sender {}

#pragma mark - Private
- (void)initUI {}  //UI初始化方法
- (void)loadData {}  //加载数据方法
- (void)displayInfo {} //加载完UI显示数据方法
- (void)privateMethod {}  //其他私有业务逻辑等方法

#pragma mark - Public
- (void)publicMethod {}
- 
#pragma mark - Protocol conformance
#pragma mark - UITextFieldDelegate
#pragma mark - UITableViewDataSource
#pragma mark - UITableViewDelegate
#pragma mark - NSCopying
- (id)copyWithZone:(NSZone *)zone {}
#pragma mark - NSObject
- (NSString *)description {}
#pragma mark – getters and setters
- (void)setCustomProperty:(id)value {}
- (id)customProperty {}

2.2 基本格式

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

说明:这样可以防止书写失误,也易于阅读。

正例:
if (varible1 < varible2) 
{
varible1 = varible2;
}
反例:下面的代码执行语句紧跟if的条件之后,而且没有加{},违反规则。

if (varible1 < varible2) varible1 = varible2;  
不使用上面的,使用K&R风格 (习惯这种)
if (user.isHappy) {
    //Do something
} else {
    //Do something
}
2.2.2 定义指针类型的变量,*应放在变量前。
正例:
float  *pfBuffer;
反例:
float*  pfBuffer;
2.2.3 源程序中关系较为紧密的代码应尽可能相邻。

说明:这样便于程序阅读和查找。

正例:
iLength     = 10;
iWidth      = 5;     // 矩形的长与宽关系较密切,放在一起。
StrCaption  = “Test”;
反例:
iLength = 10;
strCaption  = “Test”; 
iWidth      = 5;
2.2.4 Case语句

大括号在case语句中并不是必须的,除非编译器强制要求。当一个case语句包含多行代码时,大括号应该加上。

switch (condition) {  
  case 1:  
    // ...  
    break;  
  case 2: {  
    // ...  
    // Multi-line example using braces  
    break;  
  }  
  case 3:  
    // ...  
    break;  
  default:   
    // ...  
    break;  
}  
有很多次,当相同代码被多个cases使用时,一个fall-through应该被使用。一个fall-through就是在case最后移除’break’语句,这样就能够允许执行流程跳转到下一个case值。为了代码更加清晰,一个fall-through需要注释一下。
switch (condition) {  
  case 1:  
    // ** fall-through! **  
  case 2:  
    // code executed for values 1 and 2  
    break;  
  default:   
    // ...  
    break;  
}  
当在switch使用枚举类型时,’default’是不需要的。例如:
RWTLeftMenuTopItemType menuType = RWTLeftMenuTopItemMain;  
switch (menuType) {  
  case RWTLeftMenuTopItemMain:  
    // ...  
    break;  
  case RWTLeftMenuTopItemShows:  
    // ...  
    break;  
  case RWTLeftMenuTopItemSchedule:  
    // ...  
    break;  
} 
2.2.5 三元操作符

当需要提高代码的清晰性和简洁性时,三元操作符?:才会使用。单个条件求值常常需要它。多个条件求值时,如果使用if语句或重构成实例变量时,代码会更加易读。一般来说,最好使用三元操作符是在根据条件来赋值的情况下。
Non-boolean的变量与某东西比较,加上括号()会提高可读性。如果被比较的变量是boolean类型,那么就不需要括号。

应该:
NSInteger value = 5;   result = (value != 0) ? x : y; 
BOOL isHorizontal = YES;   result = isHorizontal ? x : y;   
不应该:
result = a > b ? x = c > d ? c : d : y; 
2.2.6 表达式与语句

表达式是语句的一部分,它们是不可分割的。表达式和语句虽然看起来比较简单,但使用时隐患比较多。归纳了正确使用表达式和if、for、while、goto、switch等基本语句的一些规则与建议。

  • 一条语句只完成一个功能。
    • 说明:复杂的语句阅读起来,难于理解,并容易隐含错误。变量定义时,一行只定义一个变量。
正例:
int  iHelp;    
int  iBase;
int  iResult;

iHelp   = iBase;
iResult = iHelp + GetValue(&iBase);
反例:
int iBase, iResult;                 // 一行定义多个变量 
iResult = iBase + GetValue(&iBase); // 一条语句实现多个功能,iBase有两种用途。
  • 在表达式中使用括号,使表达式的运算顺序更清晰。
    • 说明:由于将运算符的优先级与结合律熟记是比较困难的,为了防止产生歧义并提高可读性,即使不加括号时运算顺序不会改变,也应当用括号确定表达式的操作顺序。
正例: 
if  (((iYear % 4 == 0) && (iYear % 100 != 0)) || (iYear % 400 == 0))
反例:
if (iYear % 4 == 0 && iYear % 100 != 0 || iYear % 400 == 0)
  • 避免表达式中的附加功能,不要编写太复杂的复合表达式。
    • 说明:带附加功能的表达式难于阅读和维护,它们常常导致错误。对于一个好的编译器,下面两种情况效果是一样的。
正例 
aiVar[1] = aiVar[2] + aiVar[3];
aiVar[4]++;
iResult = aiVar[1] + aiVar[4];
aiVar[3]++;
反例:   
iResult = (aiVar[1] = aiVar[2] + aiVar[3]++) + ++aiVar[4] ; 
  • 不可将布尔变量和逻辑表达式直接与YES、NO或者1、0进行比较。
    • 说明:TURE和FALSE的定义值是和语言环境相关的,且可能会被重定义的。
正例:
设bFlag 是布尔类型的变量
if (bFlag)      // 表示flag为真
if (!bFlag)     // 表示flag为假
反例:
设bFlag 是布尔类型的变量

if (bFlag == TRUE)  
if (bFlag == 1)     
    if (bFlag == FALSE)  
    if (bFlag == 0)     
  • 在条件判断语句中,当整型变量与0 比较时,不可模仿布尔变量的风格,应当将整型变量用“==”或“!=”直接与0比较。
正例:
if (iValue == 0)  

if (iValue != 0)

反例:
if (iValue)     // 会让人误解 iValue是布尔变量
if (!iValue) 
  • 不可将浮点变量用“==”或“!=”与任何数字比较。
    • 说明:无论是float还是double类型的变量,都有精度限制。所以一定要避免将浮点变量用“==”或“!=”与数字比较,应该转化成“>=”或“<=”形式。
正例:
if ((fResult >= -EPSINON) && (fResult <= EPSINON))
反例:
if (fResult == 0.0)     // 隐含错误的比较
其中EPSINON是允许的误差(即精度)。
  • 应当将指针变量用“==”或“!=”与nil比较。
    • 说明:指针变量的零值是“空”(记为NULL),即使NULL的值与0相同,但是两者意义不同。
正例:
if (pHead == nil)     // pHead与NULL显式比较,强调pHead是指针变量
if (pHead != nil)   
反例:
if (pHead == 0)           // 容易让人误解pHead是整型变量
if (pHead != 0)    
或者
if (pHead)            // 容易让人误解pHead是布尔变量
if (!pHead)
  • 在switch语句中,每一个case分支必须使用break结尾,最后一个分支必须是default分支。
    • 说明:避免漏掉break语句造成程序错误。同时保持程序简洁。
    • 对于多个分支相同处理的情况可以共用一个break,但是要用注释加以说明。
正例:
          switch (iMessage)
          {
case SPAN_ON:
{
                    [处理语句]
                    break;
               }
              case SPAN_OFF:
              {
                    [处理语句]
                    break;
              }
              default:
              {
                     [处理语句]
break;
              }    
          }
  • 不可在for 循环体内修改循环变量,防止for 循环失去控制。

  • 循环嵌套次数不大于3次。

  • do while语句和while语句仅使用一个条件。

    • 说明:保持程序简洁。如果需要判断的条件较多,建议用临时布尔变量先计算是否满足条件。
正例:
     BOOL bCondition;

     do
     {
         ……..
bCondition = ((tAp[iPortNo].bStateAcpActivity != PASSIVE)
               || (tAp[iPortNo].bStateLacpActivity != PASSIVE)) 
                   && (abLacpEnabled[iPortNo])
                   && (abPortEenabled[iPortNo])

      } while (bCondition);
  • 如果循环体内存在逻辑判断,并且循环次数很大,宜将逻辑判断移到循环体的外面。
    • 说明:下面两个示例中,反例比正例多执行了NUM -1次逻辑判断。并且由于前者总要进行逻辑判断,使得编译器不能对循环进行优化处理,降低了效率。如果NUM非常大,最好采用正例的写法,可以提高效率。
const  int NUM = 100000;
正例:
if (bCondition)
{
for (i = 0; i < NUM; i++)
{
         doSomething();
     }
}
else
{
for (i = 0; i < NUM; i++)
{
         doOtherthing();
    }
}

反例:   
for (i = 0; i < NUM; i++)
{
if (bCondition)
    {
        DoSomething();
}
else
{
        DoOtherthing();
    }
}
  • for语句的循环控制变量的取值采用“半开半闭区间”写法。
    • 说明:这样做更能适应c语言数组的特点,c语言的下标属于一个“半开半闭区间”。
正例:
int  aiScore[NUM];
…
for (i = 0; i < NUM; i++)
{
     printf(“%d\n”,aiScore[i])
}
反例:
int  aiScore[NUM];
…
for (i = 0; i <= NUM-1; i++)
{
     printf(“%d\n”,aiScore[i]);
}
相比之下,正例的写法更加直观,尽管两者的功能是相同的。
  • 黄金路径
    • 当使用条件语句编码时,左手边的代码应该是”golden” 或 “happy”路径。也就是不要嵌套if语句,多个返回语句也是OK。
应该:
- (void)someMethod {
     if (![someOther boolValue]) {
       return;     
}     
//Do something important   
}   
不应该:
- (void)someMethod {
     if ([someOther boolValue]) {
       //Do something important
     }   
} 

2.3 对齐

2.3.1 禁止使用TAB键,必须使用空格进行缩进。缩进为4个空格。
  • 说明:消除不同编辑器对TAB处理的差异,有的代码编辑器可以设置用空格代替TAB键。
2.3.2 结构型的数组、多维的数组如果在定义时初始化,按照数组的矩阵结构分行书写。
正例:
int aiNumbers[4][3] = 
{
1, 1, 1,
2, 4, 8,
3, 9, 27,
4, 16, 64
}
2.3.3 相关的赋值语句等号对齐。
正例:
tPDBRes.wHead       =  0;
tPDBRes.wTail       =  wMaxNumOfPDB - 1;
tPDBRes.wFree       =  wMaxNumOfPDB;
tPDBRes.wAddress    =  wPDBAddr;
tPDBRes.wSize       =  wPDBSize;

2.4 空行空格

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

说明:空行起着分隔程序段落的作用。适当的空行可以使程序的布局更加清晰。

正例:
(void)hey
{
[hey实现代码]
}
// 空一行
// 空一行
(void)ack
{
[ack实现代码]
}
反例:
void Foo::Hey(void)
{
[Hey实现代码]
}
void Foo::Ack(void)
{
[Ack实现代码]
}
// 两个函数的实现是两个逻辑程序块,应该用空行加以分隔。
2.4.2 元运算符、强制类型转换

一元操作符如“!”、“~”、“++”、“–”、“*”、“&”(地址运算符)等前后不加空格。“[]”、“.”、“->”这类操作符前后不加空格。

正例:
!bValue
~iValue
++iCount
*strSource
&fSum
aiNumber[i] = 5;
tBox.dWidth 
tBox->dWidth 
NewType a = (NewType)b;
2.4.3 多元运算符和它们的操作数之间至少需要一个空格。
正例:
fValue  =  fOldValue;
fTotal  +  fValue
iNumber +=  2;
2.4.4 关键字之后要留空格。

说明:if、for、while等关键字之后应留一个空格再跟左括号‘(’,以突出关键字。

2.4.5 函数名之后不要留空格。

说明:函数名后紧跟左括号‘(’,以与关键字区别。

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

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

正例:
-凵(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation 
{
    // Return YES for supported orientations.
    return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
2.4.7‘(’,‘)’、‘,’、‘;’里的空格

‘(’向后紧跟,‘)’、‘,’、‘;’向前紧跟,紧跟处不留空格。‘,’之后要留空格。‘;’不是行结束符号时其后要留空格

正例:
例子中的 凵 代表空格。 
for凵(i凵=凵0;凵i凵<凵MAX_BSC_NUM;凵i++)
{
DoSomething(iWidth,凵iHeight);
}
2.4.8 注释符与注释内容之间要用一个空格进行分隔。

正例:
/* 注释内容 */
// 注释内容
反例:
/注释内容/
//注释内容

2.4.9 数组和字典类型的字面值的方括号两边各放置一个空格。
NSArray *theShit = @[ @1, @2, @3 ];
字典字面值的键和冒号之间没有空格,冒号和值之间有一个空格。
NSDictionary *keyedShit = @{ GHDidCreateStyleGuide: @YES };
C函数声明中,左括号的前面不保留空格,并且函数名应该像类一样带有命名空间标识。
良好的风格:
void RNCwesomeFunction(BOOL hasSomeArgs);
长的字面值应被拆分为多行。

良好的风格:
NSArray *theShit = @[
    @"Got some long string objects in here.",
    [AndSomeModelObjects too],
    @"Moar strings."
];

NSDictionary *keyedShit = @{
    @"this.key": @"corresponds to this value",
    @"otherKey": @"remoteData.payload",
    @"some": @"more",
    @"JSON": @"keys",
    @"and": @"stuff",
};
每一行代码使用4个空格缩进。不使用tab缩进。下图是在Xcode的Preferences进行缩进设置的截图。
如果一个方法内有多个功能区域,可以使用空行分隔功能区域;

2.5 断行

2.5.1 长表达式
  • 长表达式(超过100列)要在低优先级操作符处拆分成新行,操作符放在新行之首(以便突出操作符)。拆分出的新行要进行适当的缩进,使排版整齐。通过设置 Xcode > Preferences > Text Editing > Show page guide.,来使越界更容易被发现。
  • 说明:条件表达式的续行在第一个条件处对齐。
    • for循环语句的续行在初始化条件语句处对齐。
    • 函数调用和函数声明的续行在第一个参数处对齐。
    • 赋值语句的续行应在赋值号处对齐。
正例:
if ((iFormat == CH_A_Format_M) 
&& (iOfficeType == CH_BSC_M)) // 条件表达式的续行在第一个条件处对齐
{ 
doSomething();
}

for (long_initialization_statement;
long_condiction_statement;     // for循环语句续行在初始化条件语句处对齐
long_update_statement)
{
        doSomething();
}

// 函数声明的续行在第一个参数处对齐
BYTE ReportStatusCheckPara(HWND hWnd, 
                    BYTE ucCallNo, 
                    BYTE ucStatusReportNo);

// 赋值语句的续行应在赋值号处对齐
fTotalBill = fTotalBill + faCustomerPurchases[iID]
+ fSalesTax(faCustomerPurchases[iID]);
2.5.2 函数(方法)声明时,类型与名称不允许分行书写。
正例:
extern double FAR CalcArea(double dWidth, double dHeight);
反例:
extern double FAR 
CalcArea(double dWidth, double dHeight);

3. 注释

  • 注释有助于理解代码,有效的注释是指在代码的功能、意图层次上进行注释,提供有用、额外的信息,而不是代码表面意义的简单重复。
    • 多行注释采用“/* … */”,单行注释采用“// …”。
    • 建议 不管多行还是单行,采用注释符“/* … */”。
  • 一般情况下,源程序有效注释量必须在30%以上。
    • 说明:注释的原则是有助于对程序的阅读理解,注释不宜太多也不能太少,注释语言必须准确、易懂、简洁。有效的注释是指在代码的功能、意图层次上进行注释,提供有用、额外的信息。

3.1 文件头部必须进行注释,包括:.h文件、.c文件、.m文件、.inc文件、.def文件、编译说明文件.cfg等。

  • 说明:注释必须列出:版权信息、文件标识、内容摘要、版本号、作者、完成日期、修改信息等。修改记录部分建议在代码做了大修改之后添加修改记录。备注:文件名称,内容摘要,作者等部分一定要写清楚。
正例:
下面是文件头部的中文注释:
/*************************************************************
* 版权所有 (C)2011中兴软件技术(南昌)有限公司
* 
* 文件名称: // 文件名
* 文件标识: // 见配置管理计划书
* 内容摘要: // 简要描述本文件的内容,包括主要模块、函数及其功能的说明
* 其它说明: // 其它内容的说明
* 当前版本: // 输入当前版本
* 作    者: // 输入作者名字及单位
* 完成日期: // 输入完成日期,例:2011年11月29日

* 修改记录1:// 修改历史记录,包括修改日期、修改者及修改内容
*    修改日期:
*    版 本 号://或版本号
*    修 改 人:
*    修改内容://修改原因以及修改内容说明
* 修改记录2:…
**************************************************************/
3.2 方法头部应进行注释,列出:函数的目的/功能、输入参数、输出参数、返回值、访问和修改的表、修改信息等,除了函数(方法)名称和功能描述必须描述外,其它部分建议写描述。
说明:注释必须列出:函数名称、功能描述、输入参数、输出参数、返 回 值、修改信息等。备注:方法名称、功能描述要正确描述。
正例:
/*************************************************************
* 方法名称: // 方法名称
* 功能描述: // 方法功能、性能等的描述
* 输入参数: // 输入参数说明,包括每个参数的作用、取值说明及参数间关系
* 输出参数: // 对输出参数的说明。
* 返 回 值: // 方法返回值的说明 
* 其它说明: // 其它说明
**************************************************************/

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

说明:在使用缩写时或之前,应对缩写进行必要的说明。

正例: 
如下书写比较结构清晰

/* 获得子系统索引 */
iSubSysIndex = aData[iIndex].iSysIndex;

/* 代码段1注释 */
[ 代码段1 ]

/* 代码段2注释 */
[ 代码段2 ]
反例1:
如下例子注释与描述的代码相隔太远。
/* 获得子系统索引 */

iSubSysIndex = aData[iIndex].iSysIndex;
反例2:
如下例子注释不应放在所描述的代码下面。

iSubSysIndex = aData[iIndex].iSysIndex; 
/* 获得子系统索引 */
反例3:
如下例子,显得代码与注释过于紧凑。
/* 代码段1注释 */
[ 代码段1 ]
/* 代码段2注释 */
[ 代码段2 ]

3.4 全局变量要有详细的注释,包括对其功能、取值范围、访问信息及访问时注意事项等的说明。

正例:
/*
* 变量作用说明
* 变量值说明
*/
BYTE g_ucTranErrorCode;
3.5 注释与所描述内容进行同样的缩排。
说明:可使程序排版整齐,并方便注释的阅读与理解。
正例: 
如下注释结构比较清晰
-   (int)doSomething
{
/* 代码段1注释 */
    [ 代码段1 ]

    /* 代码段2注释 */
    [ 代码段2 ]
} 

反例:
如下例子,排版不整齐,阅读不方便;
int DoSomething(void)
{
/* 代码段1注释 */
    [ 代码段1 ]

/* 代码段2注释 */
    [ 代码段2 ]
}
  • 尽量避免在注释中使用缩写,特别是不常用缩写。
  • 说明:在使用缩写时,应对缩写进行必要的说明。

4. 命名规则

4.1 基本规则

好的命名规则能极大地增加可读性和可维护性。同时,对于一个有上百个人共同完成的大项目来说,统一命名约定也是一项必不可少的内容。本章对程序中的所有标识符(包括变量名、常量名、函数名、类名、结构名、宏定义等)的命名做出约定。

4.1.1 标识符
  • 说明:标识符要采用英文单词或其组合,便于记忆和阅读,切忌使用汉语拼音来命名。应当直观且可以拼读,可望文知义,避免使人产生误解。程序中的英文单词一般不要太复杂,用词应当准确。
    严格禁止使用连续的下划线,下划线也不能出现在标识符头或结尾(预编译开关除外)。
  • 说明:这样做的目的是为了使程序易读。因为 variable_name 和 variable__name 很难区分,下划线符号‘’若出现在标识符头或结尾,容易与不带下划线‘’的标识符混淆。
  • 程序中不要出现仅靠大小写区分的相似的标识符。
  • 宏、常量名都要使用大写字母, 用下划线 ‘’ 分割单词。预编译开关的定义使用下划线 ‘’ 开始。
  • 正例:如 DISP_BUF_SIZE、MIN_VALUE、MAX_VALUE 等等。
4.1.2 程序中局部变量不要与全局变量重名。

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

4.1.3 使用一致的前缀来区分变量的作用域。
说明:变量活动范围前缀规范如下(少用全局变量):
   g_      :  全局变量
   s_      :  模块内静态变量
   空      :  局部变量不加范围前缀
4.1.4 方法名用小写字母开头的单词组合而成。(小驼峰)

说明:方法名力求清晰、明了,通过方法名就能够判断方法的主要功能。方法名中不同意义字段之间不要用下划线连接,而要把每个字段的首字母大写以示区分。方法命名采用大小写字母结合的形式,但专有名词不受限制。

4.1.5 类中的属性以小写字母m_开头。

说明:参考匈牙利命名规则,常见的简写如下:

整型          n
指针          p
字符串         str
布尔          b
字符          c
函数          fn
正例: 
@interface CMainMenu
{
    int m_nWidth;
    NString *m_strName;
    BOOL m_bCheck;
}
建议1 尽量避免名字中出现数字编号,如Value1、Value2等,除非逻辑上的确需要编号。
建议2标识符前最好不加项目、产品、部门的标识。
说明:这样做的目的是为了代码的可重用性。
4.1.5 前缀
  • 前缀在编程接口中是非常重要的一部分。一个软件有不同的功能模块,通常它们封装在一个框架或者相近的框架中。前缀避免了第三方开发者和Apple之间的命名冲突。
  • 前缀有规定的格式。通常由2/3个大写字母组成,不是用下划线和子前缀。例如:NS、IB、AB。
  • 使用前缀来命名类、协议、函数、常数,自定义数据类型(typedef structures),不要用前缀来命名方法。方法存在类的命名区域中,不要在这区域里面使用前缀。

4.2 资源命名

  • 字符串:以“IDS_”开头,如:IDS_VIEW
  • 图片:以“IDB_”开头,如:IDB_COREICON
  • 图片的命名
    • 图片的命名应该保持一致,以图片的用途描述作为图片文件名。文件名的命名使用驼峰式大小写风格,文件名后可跟随一个自定义的类名或者是自定义的属性名(如果有属性名)、也可以再跟上颜色描述以及/或者位置、图片的最终状态。
良好的风格:
RefreshBarButtonItem / RefreshBarButtonItem@2xRefreshBarButtonItemSelected / RefreshBarButtonItemSelected@2x
ArticleNavigationBarWhite / ArticleNavigationBarWhite@2xArticleNavigationBarBlackSelected /
 ArticleNavigationBarBlackSelected@2x.
被用作相似用途的图片应该使用一个图片文件夹进行分开管理。

5. 类、类别(扩展)、代理

5.1 类和协议名称

  • 一个类的名称应该包含一个名词,清楚地表明的类(或类的对象)作用或者意义。名称应该有一个适当的前缀(参考“前缀”小节)。在框架中类名的例子比比皆是:NSString,NSDate,nsscanner,NSApplication,UIApplication,NSButton, and UIButton。
  • 协议的命名应该根据使用协议的相应类行为命名。大多数协议包含的相关方法,不与任何特定的类关联。这种协议的应该命名为使协议与类不能混淆。一个通常的规则是用动名词(…ing)。对比NSLocking 、NSLock(看起来像类名)。有的协议包含一些没什么联系的方法(而不是创建多个独立的小协议)。这些协议跟一个类的联系很大,这个类主要体现了这个协议。这种情况下,命名规则为协议名跟类名字一样。一个例子是NSObject 协议,这个协议包含一些方法可以查询任何类在父类中的层次位置等。因为NSObject类实现了协议的大部分方法,所以协议可以以类名命名。
  • 由于Objective-C不支持名字空间,为了防止出现命名空间的冲突,在类名和常类型变量名前添加一个由三个大写的字母组成的前缀(如RNC),对于Core Data实体名则可以忽略此规则。如果你子类化了标准的Cocoa类,将前缀和父类名合并是一个很好的做法。如继承UITableView的类可命名为RNCTableView。

5.2 类别名

  • 类别名应该有两三个字母的前缀以表示类别是项目的一部分或者该类别是通用的。类别应该包含它所扩展的类的名字。
    • 例如,录我们要创建一个NSString的类别以解析时,我们将把类别放在一个名为GTMNSString+Parsing.h的文件中。
  • 类别名本身的名字是GTMStringParsingAdditions(是的,我们知道类别名和文件名不一样,但是这个文件中可能存在多个不同的与解析有关类别)。
  • 类别中的方法应该以gtm_myCategoryMethodOnAString为前缀以避免命名冲突,因为Objective-C只有一个命名空间。
  • 如果代码不会被分享并且不会被运行在不同的地址空间中,方法名字就不那么重要。
  • 类名与包含类别名的括号之间,应该以一个空格分隔。
  • 如果一个delegate只有几个方法,比如只是提交和取消,推荐使用block编写动作响应代码。

5.3 类、类别、协议的头文件

  • 怎么命名你的头文件非常重要。因为你的命名表明了类中的内容:
    • 声明一个独立的类/协议:如果一个类/协议不是一个文件中的一部分,将其声明独立成一个文件,这个文件的名字表明了该类/协议;
    • 声明联系的类/协议:如果有一些联系的声明(类、协议、分类),将它们声明放到一个文件中,文件的命名根据基础的类、协议、分类;
头文件
声明
NSString.h
NSString和NSSMutableString
NSLock.h
NSLocking协议、NSLock、NSConditionLock、NSRecursive类
包含框架的头文件:所有的框架都有一个头文件,以框架命名,包含框架里所有公开的头文件。例Foundation.h-- Foundation.framwork。 
为别的框架中类增加API:如果你在一个框架中声明的方法,是另一个框架中类的分类,名字为原来类的名字拼接上“Additions”。一个例子为Applicatiion kit 的NSBuddleAdditions.h头文件。 相联系的函数和数据类型:如果你有一些相联系的函数、常数、结构体等其他数据类型,将它们放到合适命名的头文件中。例如NSGraphics.h(Applicatiion kit )。
命名分类(比如RWTPrivate或private)应该从不使用除非是扩展其他类。匿名分类应该通过使用+Private.h文件的命名规则暴露给测试。

6. 属性,变量,常量,宏与类型

  • 变量、常量和数据类型是程序编写的基础,它们的正确使用直接关系到程序设计的成败,变量包括全局变量、局部变量和静态变量,常量包括数据常量和指针常量,类型包括系统的数据类型和自定义数据类型。本章主要说明变量、常量与类型使用时必须遵循的规则和一些需注意的建议,关于它们的命名,参见命名规则。

6.1属性、实例变量

6.1.1 属性和实例变量命名
  • 因为属性和存取器方法的对应性质(get方法和set后那部分名称即是属性名字,对应实例变量),所以对于属性的命名基本类似存取器方法的名字,参考存取器方法小节。
 如果属性是名称/动词意思,
 格式是:@property (…) type nounOrVerb 
 例:@property (strong) NSString *title; 
 @property (assign) BOOL isShowsAlpha; 
  • 如果属性是形容词意思,属性名称省略is前缀,并且指定存取器get方法的命名。
    例如:@property (assign, getter=isEditable) BOOL editable; 
  • 在许多情况,当你声明一个了属性,你同时也确定(synthesize)了相应的实例变量。 确保实例变量简明的描述存储的属性,通常你不直接访问实例变量,而是通过存取器方法(在类内部直接访问),为了区别,用下划线前缀;
例:
@implementation MyClass {
BOOL _showsTitle;
}
  • 如果你想用某实例变量对应某个属性,在@synthesize中说明:@implementation MyClass @synthesize showsTitle=_showsTitle;
  • 当添加实例变量的时候,有以下几条规则:避免显示的声明公共的实例变量。开发者只会关心对象的接口,不关心实现的细节。通过声明属性和相应的(synthesizing)实例变量,避免显示声明实例变量。如果需要声明实例变量,用@private 或者 @protected声明。如要继承的实例变量用@protected声明。如果一个实例变量是实例的访问属性(accessible attribute),确保你已经写了相应的存取器方法。
  • *

6.1.2 属性的访问()

  • 当使用属性时,实例变量应该使用self.来访问和改变。这就意味着所有属性将会视觉效果不同,因为它们前面都有self.。
  • 尽量的使用属性而非实例变量。除了在初始化方法(init,initWithCoder:等)、dealloc方法以及自定义setter与getter方法中访问属性合成的实例变量,其他的情况使用属性进行访问。

6.1.3 属性修饰符

  • 所有属性特性应该显式地列出来,有助于新手阅读代码。属性特性的顺序应该是storage、atomicity,与在Interface Builder连接UI元素时自动生成代码一致。
应该:
@property (weak, nonatomic) IBOutlet UIView *containerView;   @property (strong, nonatomic) NSString *tutorialName;   
不应该:
@property (nonatomic, weak) IBOutlet UIView *containerView;   @property (nonatomic) NSString *tutorialName;  
一定要注意属性的开销。所有synthesize的setter和getter都是原子的。这会给每个get或者set带来一定的同步开销。显示将你的属性声明为nonatomic除非你需要原子操作。
6.1.4 私有属性

私有属性应该被声明在实现文件的类扩展中(即匿名的category)。不要将私有属性声明在命名的category(如RNCPrivate或private),除非是扩展其他类。

@public与@private
 @public以及@private访问标识符应该以一个空格缩进。
与C++中的public, private,以及protected非常相似。
@interface MyClass : NSObject {
 @public
  ...
 @private
  ...
}
@end
6.1.5 NSString使用copy特性(看情况,待定)

NSString应该使用copy而不是strong的属性特性。
为什么?即使你声明一个NSString的属性,有人可能传入一个NSMutableString的实例,然后在你没有注意的情况下修改它。

应该:
@property (copy, nonatomic) NSString *tutorialName;   
不应该:
@property (strong, nonatomic) NSString *tutorialName; 
6.1.6 点语法
  • 点引用是地道的Objective-C 2.0的风格。它被使用于简单的属性set、get操作,但对象的其它行为不应该使用它。
  • 点语法是一种很方便封装访问方法调用的方式。当你使用点语法时,通过使用getter或setter方法,属性仍然被访问或修改。点语法应该总是被用来访问和修改属性,因为它使代码更加简洁。[]符号更偏向于用在其他例子。
  • 属性和幂等方法(多次调用和一次调用返回的结果相同)使用点标记语法访问,其他的情况使用方括号标记语法。
良好的风格:
view.backgroundColor = [UIColor orangeColor];
[UIApplication sharedApplication].delegate;
不良的风格:
[view setBackgroundColor:[UIColor orangeColor]];
UIApplication.sharedApplication.delegate;

6.2 变量

一个变量只用来表示一个特定功能,不能把一个变量作多种用途,即同一变量取值不同时,其代表的意义也不同。最好不要在语句块内声明局部变量。

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

循环语句只完成循环控制功能,if语句只完成逻辑判断功能,不能完成计算赋值功能。

正例:
do
{
             [处理语句]
              cInput = GetChar();
         } while (cInput == 0);
 反例:
         do
{
             [处理语句]
 } while (cInput = GetChar());
6.2.2 全局变量通过统一的函数访问。(尝试使用该方案)

说明:可以避免访问全局变量时引起的错误。

正例:
T_Student    g_tStudent;
T_Student GetStudentValue(void)
      {
        T_Student tStudentValue;
        [获取g_tStudent的访问权]
        tStudentValue = g_tStudent;
        [释放g_tStudent的访问权]
          return tStudentValue;
}
BYTE SetStudentValue(const T_Student  *ptStudentValue)
{
    BYTE ucIfSuccess;
    ucIfSuccess = 0;
    [获取g_tStudent的访问权]
    g_tStudent = *ptStudentValue ;
    [释放g_tStudent的访问权]
    return ucIfSuccess;
}

6.2.3 变量的命名

  • 常类型变量名的书写风格采用驼峰式大小写(第一个单词的首字母小写,其余单词的第一个字母大写。如firstName而不是first_name或firstname。),并使用关联的类名作为其命名前缀,
    变量的命令应尽量做到自描述。
  • 除了在for()循环语句中,单字母的变量应该避免使用(如i,j,k等)。一般循环语句的当前对象的命名前缀包括“one”、“a/an”。对于简单的单个对象使用“item”命名。
  • 对于NSString、NSArray、NSNumber或BOOL类型,变量的命名一般不需要表明其类型。//数组用List、s,推荐List
良好的风格:
NSString       *accountName;
NSMutableArray *mailboxes;
NSArray        *defaultHeaders;
BOOL            userInputWasUpdated;
不良的风格:
NSString        *accountNameString;
NSMutableArray *mailboxArray;
NSArray        *defaultHeadersArray;
BOOL             userInputWasUpdatedBOOL;
  • 如果变量不是以上基本常用类型,则变量的命名就应该反映出自身的类型。但有时仅需要某些类的一个实例的情况下,那么只需要基于类名进行命名。
NSImage               *previewPaneImage;
NSProgressIndicator  *uploadIndicator;
NSFontManager        *fontManager;       // 基于类名命名
  • 大部分情况下,NSArray或NSSet类型的变量只需要使用单词复数形式(比如mailboxes),不必在命名中包含“mutable”。如果复数变量不是NSArray或NSSet类型,则需要指定其类型。
良好的风格:
NSDictionary * keyedAccountNames;
NSDictionary * messageDictionary;
NSIndexSet   * selectedMailboxesIndexSet;
  • 控件命名必须加控件后缀。UIButton *settingsButton;

6.3 常量

是容易重复被使用和无需通过查找和代替就能快速修改值。常量应该使用static来声明而不是使用#define,除非显式地使用宏。

6.3.1 常量的命名
  • 常数的命名规则跟常数是怎么产生的息息相关。
    • 宏:应该用全大写加下划线方式;
    • 枚举:Enum类型的命名与类的命名规则一致;
    • 静态局部常量:应该以小写字母k开头,使用混合大小写的格式来分隔单词;
//UserDefalut Key
//使用前缀 kUDKey标明
static NSString *constk UDKeyPrivacySwitchState = 
@"kUDKeyPrivacySwitchState";
//Notification Key
//使用小写k开头、以及Notification结尾
static NSString * const kAddDiarySucceedNotificaiton = 
@"kAddDiarySucceedNotificaiton";
const int kNumberOfFiles = 12;
NSString *const kUserKey = @"kUserKey";
enum DisplayTinge {
  kDisplayTingeGreen = 1,
  kDisplayTingeBlue = 2
};
  • 因为Objective-C没有提供命名空间,全局范围内的常量应该有一个适当的前缀来减小命名冲突,典型的类似kClassNameFoo。

    • const修饰的常数
      使用const去创建浮点型常量,可以创建整形常量,如果各整形常量之间没有什么联系,否则,使用枚举。const修饰的常数命名规则,举例说明:const float NSLightGray; 命名规则类似函数,参考函数命名小节。
    • 枚举常数
      对于有取值相联系的常数集合,使用枚举(说什么情况使用枚举,跟命名没关系)。枚举常数和typedef后面枚举名的命名跟函数的命名规则类似,参考函数命名小节。
例:
枚举应该使用下面这种方式
typedef enum _NSMatrixMode {
NSRadioModeMatrix = 0,
NSHighlightModeMatrix = 1,
NSListModeMatrix = 2,
NSTrackModeMatrix = 3
} NSMatrixMode;
注意上文中_NSMatrixMode 在typedef中没有用。
你也可以使用不命名的枚举,比如位掩码(bit masks),例如:
enum {
NSBorderlessWindowMask = 0,
NSTitledWindowMask = 1 << 0,
NSClosableWindowMask = 1 << 1,
NSMiniaturizableWindowMask = 1 << 2,
NSResizableWindowMask = 1 << 3
};
枚举第一个成员要赋初始值。
正例:
typedef enum
{
    WIN_SIZE_NORMAL = 0,
    WIN_SIZE_SMALL
}WinSize;
反例: 
typedef enum
{
 WIN_SIZE_NORMAL,
    WIN_SIZE_SMALL
}WinSize;
  • 容器常量
    使用Xcode 4.4或更高版本的的项目,鼓励使用容器常量。如果被分成了多行,其内容需两个空白字符缩进。
如果集合内容只有一行,开始中括号[之后和结束中括号]之前需要一个空格。
NSArray* array = @[ [foo description], @"Another String", [bar description] ];
NSDictionary* dict = @{ NSForegroundColorAttributeName : [NSColor redColor] };
而不是:
NSArray* array = @[[foo description], [bar description]];
NSDictionary* dict = @{NSForegroundColorAttributeName: [NSColor redColor]};
如果集合跨越超过一行,左中括号[单独位于声明的那一行,内容缩进两个空格,右括号位于新行并与声明左中括号的行开头对齐。
NSArray* array = @[
  @"This",
  @"is",
  @"an",
  @"array"
];
NSDictionary* dictionary = @{
  NSFontAttributeName : [NSFont fontWithName:@"Helvetica-Bold" size:12],
  NSForegroundColorAttributeName : fontColor
};
对于字典常量,冒号前后应该有一个空格(值是否对齐是可选的)
NSDictionary* option1 = @{
  NSFontAttributeName : [NSFont fontWithName:@"Helvetica-Bold" size:12],
  NSForegroundColorAttributeName : fontColor
};
NSDictionary* option2 = @{
  NSFontAttributeName : [NSFont fontWithName:@"Arial" size:12],
  NSForegroundColorAttributeName : fontColor
};
如下是错误的:
// There should be a space before the colon.
NSDictionary* wrong = @{
  AKey:       @"b",
  BLongerKey: @"c",
}; 
// The items should each be on a new line, or the entire expression
// should fit on one line.
NSDictionary* alsoWrong= @{ AKey : @"a",
                            BLongerKey : @"b" };

// There should be no variable space before the colon, only after.
NSDictionary* stillWrong = @{
  AKey       : @"b",
  BLongerKey : @"c",
};

6.4 宏

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

说明:在宏定义中,对表达式和变量使用括号,可以避免可能发生的计算错误。

正例:
#define  HANDLE(A, B)   (( A ) / ( B ))
反例:
#define  HANDLE(A, B)   (A / B)
6.4.2 宏名大写字母加下划线组合。
正例:
#define  BUTTON_WIDTH    (int)320
反例:
#define  kButtonWidth    (int)320
6.4.3 宏常量要指定类型。

说明:不同的编译器,默认类型不一样。

正例:
#define  BUTTON_WIDTH    (int)320
反例:
#define  BUTTON_WIDTH    320

6.5 类型

6.5.1 结构体

设计结构时应力争使结构代表一种现实事务的抽象,而不是同时代表多种。结构中的各元素应代表同一事务的不同侧面,而不应把描述没有关系或关系很弱的不同事务的元素放到同一结构中。

正例:
typedef struct TeacherStruct
{
BYTE  aucName[8];
BYTE  ucSex;    
}T_Teacher;

typedef struct StudentStruct
{
BYTE  ucName[8];   
BYTE  ucAge;       
BYTE  ucSex;     
WORD  wTeacherInd; 
}T_Student;
反例:
如下结构不太清晰、合理。
typedef struct StudentStruct
{
BYTE  aucName[8];  
BYTE  ucAge;    
BYTE  ucSex;    
BYTE  aucTeacherName[8];  
BYTE  ucTeacherSex;    
}T_Student;

6.5.2 内存对齐。

合理排列结构中元素顺序,可节省空间并增加可理解性。
正例:如下形式,不仅可节省字节空间,可读性也变好了。

typedef struct ExampleStruct
{
BYTE ucValid; 
BYTE ucSetFlg;
BYTE ucOther;  // 保留位
      T_Person  tPerson;
}T_Example;
反例:如下结构中的位域排列,将占较大空间,可读性也稍差。
typedef struct ExampleStruct
{
      BYTE    ucValid: 1;
      T_Person  tPerson;
      BYTE    ucSetFlg: 1;
} T_Example;

6.6 通知

  • 通知和异常的命名规则基本相同,但它们有各自特点。
  • 如果一个类有delegate,许多通知都会被delegate接收通过delegate方法。这些通知的名称应该反应相应的delegate方法。
  • 例如,一个全局的NSApplication类对象自动注册去接收applicationDidBecomeActive消息,当应用程序发送NSApplicationDidBecomeActiveNotification.消息的时候。
通知通过全局的字符串对象定义,格式如下:[Name of associated class] + [Did | Will] + [UniquePartOfName] + Notification; 
例:
NSApplicationDidBecomeActiveNotification
NSWindowDidMiniaturizeNotification
NSTextViewDidChangeSelectionNotification
NSColorPanelColorDidChangeNotification

7. 方法、函数、接口

7.1 方法

  • 方法不能为多个目的服务。
正例:
- (BOOL)sio_set_baud_rate:(int)arg;
- (BOOL)sio_set_stop_bits(byte)arg; 
- (BOOL)sio_set_data_bits(byte)arg; 
- (BOOL)sio_get_baud_rate:(int *)arg; 
反例:
- (BOOL)sio_ ioctl:(void *)arg;
  • 在组件接口中应该尽量少使用外部定义的类型(重用,减少耦合)。
  • 避免函数有太多的参数,参数个数尽量控制在5个以内。
    说明:如果参数太多,在使用时容易将参数类型或顺序搞错,而且调用的时候也不方便。如果参数的确比较多,而且输入的参数相互之间的关系比较紧密,不妨把这些参数定义成一个结构,然后把结构的指针当成参数输入。
  • 对于有返回值的函数(方法),每一个分支都必须有返回值。
    说明:为了保证对被调用函数返回值的判断,有返回值的函数中的每一个退出点都需要有返回值。
  • 对输入参数的正确性和有效性进行检查。
    说明:很多程序错误是由非法参数引起的,我们应该充分理解并正确处理来防止此类错误。
  • 防止将函数(方法)的参数作为工作变量。
    说明:将函数的参数作为工作变量,有可能错误地改变参数内容,所以很危险。对必须改变的参数,最好先用局部变量代之,最后再将该局部变量的内容赋给该参数。
  • 函数(方法)的功能要单一,不要设计多用途的函数(方法)。
    说明:多用途的函数往往通过在输入参数中有一个控制参数,根据不同的控制参数产生不同的功能。这种方式增加了函数之间的控制耦合性,而且在函数调用的时候,调用相同的一个函数却产生不同的效果,降低了代码的可读性,也不利于代码调试和维护。
  • 函数(方法)体的规模不能太大,尽量控制在200行代码之内。
    说明:冗长的函数不利于调试,可读性差。
7.1.1 方法声明与定义

-或者+与返回类型之间,需要有空格。参数列表中,只有参数之间有空格。

方法应该像这样:
- (void)doSomethingWithString:(NSString *)theString {
  ...
}
  • 星号前的空格是可选的。当写新的代码时,要与原的代码一致。
  • 如果一行有非常多的参数,更好的方式是将每个参数单独拆成一行。如果使用多行,将每个参数前的冒号对齐。
- (void)doSomethingWith:(GTMFoo *)theFoo
                   rect:(NSRect)theRect
               interval:(float)theInterval {
  ...

}
  • 当第一个关键字比其它的短时,保证下一行至少有4个空格的缩进。这样可以使关键字垂直对齐,而不是使用冒号对齐:
- (void)short:(GTMFoo *)theFoo
          longKeyword:(NSRect)theRect
    evenLongerKeyword:(float)theInterval
                error:(NSError **)theError {
  ...
}
7.1.2 方法调用

方法定义的格式与方法声明的格式非常相似。当格式的风格有多种选择时,新的代码要与已经存在的代码保持一致。

方法调用时,所有参数应该在同一行。
[myObject doFooWith:arg1 name:arg2 error:arg3];
或者每行一个参数,以冒号对齐:
[myObject doFooWith:arg1
               name:arg2
              error:arg3];
不要使用下面的缩进风格:
[myObject doFooWith:arg1 name:arg2  // some lines with >1 arg
              error:arg3];

[myObject doFooWith:arg1
               name:arg2 error:arg3];

[myObject doFooWith:arg1
          name:arg2  // aligning keywords instead of colons
          error:arg3];
方法调用与方法声明一样,当关键字的长度不足以以冒号对齐时,下一行都要以四个空格进行缩进。
[myObj short:arg1
          longKeyword:arg2
    evenLongerKeyword:arg3
                error:arg4];
调用包含内联 blocks时需要让它的代码段4个空格缩进左对齐
7.1.3 方法参数
  • 在命名方法参数时候有几个基本规则:
    • 参数的名字也是骆驼风格
    • 不要使用“pointer”或ptr这些词,参数的类型比参数的名字更能说明它是否是指针。
    • 避免一俩个字母做参数的名字 —-避免缩写,参数名不差多这几个字母。
 一般来讲,以下的一些方法中的关键词通常跟固定的参数搭配:
..action:(SEL)aSelector
...alignment:(int)mode
...atIndex:(int)index
...content:(NSRect)aRect
...doubleValue:(double)aDouble
...floatValue:(float)aFloat
...font:(NSFont *)fontObj
...frame:(NSRect)frameRect
...intValue:(int)anInt
...keyEquivalent:(NSString *)charCode
...length:(int)numBytes
...point:(NSPoint)aPoint
...stringValue:(NSString *)aString
...tag:(int)anInt
...target:(id)anObject
...title:(NSString *)aString
  • 参数
    • 方法参数名前一般使用的前缀包括“the”、“an”、“new”。
良好的风格:
- (void)setTitle:(NSString *) aTitle;
- (void)setName:(NSString *)newName;
- (id)keyForOption:(CDCOption *) anOption
- (NSArray *) emailsForMailbox:(CDCMailbox *) theMailbox;
- (CDCEmail *) emailForRecipients:(NSArray *) theRecipients;
7.1.4 方法命名
  • 当为方法命名时,要记住以下几点:
    • 方法名小写开头,之后每个单词首字母大写(Camel-Case),不要用前缀; 有俩种情况例外。如果方法用到了众所周知的缩写(例如TIFF或PDF) ;你可能使用前缀去统一定义私有方法,参考“私有方法”小节。
    • 如果方法代表对象某个动作,方法名用动词开头; 例如:- (void)invokeWithTarget:(id)target;
    • 不要使用do或does这样的词做名字一部分,因为这些辅助动词没什么意义,同时不要在动词前使用副词或形容词。
    • 如果方法返回的是消息发送者(对象)的属性,用属性命名方法。get这个词不需要,除非有多个间接返回的值。可以参考“存取器方法”小节。
- (NSSize)cellSize;
正确
- (NSSize)calcCellSize;
错误
- (NSSize)getCellSize;
错误
  • 在所有的参数前使用关键词
- (void)sendAction:(SEL)aSelector toObject:(id)anObject forAllCells:(BOOL)flag;
正确
- (void)sendAction:(SEL)aSelector :(id)anObject :(BOOL)flag;
错误
  • 参数前的单词描述参数的意义
- (id)viewWithTag:(NSInteger)aTag;
正确
- (id)taggedView:(int)aTag;
错误
  • 当你创建一个基于现有方法的新方法,在一个已有的方法上添加描述性的关键词参数
- (id)initWithFrame:(CGRect)frameRect;
UIView
- (id)initWithFrame:(NSRect)frameRect
           mode:(int)aMode 
cellClass:(Class)factoryId 
numberOfRows:(int)rowsHigh
numberOfColumns:(int)colsWide;
NSMatrix, a subclass of NSView
  • 不要使用and去连接多个参数的关键词(对象属性名)
- (int)runModalForDirectory:(NSString *)path
 file:(NSString *) name 
types:(NSArray *)fileTypes;
right
- (int)runModalForDirectory:(NSString *)path 
andFile:(NSString *)name
andTypes:(NSArray *)fileTypes;
wrong
尽管在这个例子中and看起来还不错,但是当方法中有许多参数的时候,再用and就不行了。 
----如果方法包含着俩个分开的动作,用and去连接它们;
例:- (BOOL)openFile:(NSString *)fullPath
        withApplication:(NSString *)appName 
andDeactivate:(BOOL)flag;
  • 方法
    • 一个方法的命名首先描述返回什么,接着是什么情况下被返回。方法签名中冒号的前面描述传入参数的类型。
以下类方法和实例方法命名的格式语法:
[object/class thing+condition];
[object/class thing+input:input];
[object/class thing+identifer:input];

Cocoa命名举例:
realPath  = [path     stringByExpandingTildeInPath];
fullString = [string   stringByAppendingString:@"Extra Text"];
object    = [array    objectAtIndex:3];
// 类方法
newString = [NSString stringWithFormat:@"%f",1.5];
newArray = [NSArray  arrayWithObject:newString];

良好的自定义方法命名风格:
recipients =  [email    recipientsSortedByLastName];
newEmail =  [CDCEmail emailWithSubjectLine:@"Extra Text"];
emails    =  [mailbox  messagesReceivedAfterDate:yesterdayDate];

当需要获取对象值的另一种类型的时候,方法命名的格式语法如下:

[object adjective+thing];
[object adjective+thing+condition];
[object adjective+thing+input:input];

良好的自定义方法命名风格:
capitalized = [name    capitalizedString];
rate        = [number  floatValue];
newString =  [string  decomposedStringWithCanonicalMapping];
subarray  =  [array   subarrayWithRange:segment];

方法签名尽量做到含义明确。
不良的风格:
-sortInfo  // 是返回排序结果还是给info做排序
-refreshTimer  // 返回一个用于刷新的定时器还是刷新定时器
-update  // 更新什么,如何更新

良好的风格:
-currentSortInfo      // "current" 清楚地修饰了名词SortInfo 
-refreshDefaultTimer  // refresh是一个动词。
-updateMenuItemTitle  // 一个正在发生的动作
7.1.5 存取器方法
  • 存取器放方法是指那些读/写对象属性的方法,根据属性意义的不同,它们有不同的通用格式。(备注:不同格式代表不同对应实例变量的写法,存取器方法形式就是intanceVariables 和 setIntanceVariables俩种形式)
  • 如果属性表示的是名词意思,格式如:
- (type)noun; - (void)setNoun:(type)aNoun; 
  • 如果属性表示的是形容词意思,格式如:
- (BOOL)isAdjective;
- (void)setAdjective:(BOOL)flag; (注意type是BOOL) 
例:- (BOOL)isEditable; 
- (void)setEditable:(BOOL)flag;
  • 如果属性表示的是动词意思,
格式如:(BOOL)verbObject; 
- (void)setVerbObject:(BOOL)flag; (注意type为BOOL)
例:- (BOOL)showsAlpha; 
- (void)setShowsAlpha:(BOOL)flag; 动词是现在时; 
  • 在属性的名称中,不要通过用分词形式将动词转换为形容词;
- (void)setAcceptsGlyphInfo:(BOOL)flag;
right
- (BOOL)acceptsGlyphInfo;
right
- (void)setGlyphInfoAccepted:(BOOL)flag;
wrong
- (BOOL)glyphInfoAccepted;
wrong
  • 可以使用情态动词(动词前面“can”、“should”、“will”等)进一步说明属性意思,但不要使用’do’或’does’。
- (void)setCanHide:(BOOL)flag;
Y
- (BOOL)canHide;
Y
- (void)setShouldCloseDocument:(BOOL)flag;
Y
- (BOOL)shouldCloseDocument;
Y
- (void)setDoesAcceptGlyphInfo:(BOOL)flag;
N
- (BOOL)doesAcceptGlyphInfo;
N
  • 当使用get这个词时,只有当方法间接返回多个对象/值。
- (void)getLineDash:(float *)pattern count:(int *)count phase:(float *)phase;

注意,这种形式的方法,其中的引用型参数应该能接收NULL,因为方法调用者可能并不需要多个返回值。

7.1.6 代理方法

代理方法是那些当发生特定事件对象使用它delegate调用的方法(如果delegate实现了它),它们有着特定的格式,这些格式也适用于对象的datesource方法。

  • 名字的开头指明发消息的对象类型。
例如:- (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(int)row; - (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename; 
类名省略了它的前缀并且小写开头。
  • 如果方法只有一个参数,格式为:冒号+类名(调用代理的对象)+sender;
例:- (BOOL)applicationOpenUntitledFile:(NSApplication *)sender; 
  • 一个例外是方法用来发送通知,如果这样的话,方法参数为通知对象;
例:- (void)windowDidChangeScreen:(NSNotification *)notification; 
  • 命名中使用did或will这类词,告诉delegate某些事情已经发生或将要发生;
例:- (void)browserDidScroll:(NSBrowser *)sender;
  • 虽然你可以在命名中是使用did或will这类词,告诉delegate去做某些事情,但有时“should”更合适;
例:- (BOOL)windowShouldClose:(id)sender;
  • 如果一个delegate只有几个方法,比如只是提交和取消,推荐使用block编写动作响应代码。
  • 由于代理方法的声明一般都很长,所以必须将代理对象和其他的协议对象放在实例变量定义的下面,否则实例变量定义的对齐方式将会被打乱掉。
  • 尖括号所包括的协议名称与前面的类型标识之间不应该有空格。
  • 这条规则也同样适用于类声明、成员变量以及方法声明。
例如:
@interface MyProtocoledClass : NSObject<NSWindowDelegate> {
 @private
  id _delegate;
}
- (void)setDelegate:(id)aDelegate;
@end
7.1.7 集合方法(Collection Method)
  • 要管理对象(每一个叫做对象的元素)的集合,命名方法以下格式:
- (void)addElement:(elementType)anObj;
- (void)removeElement:(elementType)anObj;
- (NSArray *)elements;
例如: 
- (void)addLayoutManager:(NSLayoutManager *)obj;
- (void)removeLayoutManager:(NSLayoutManager *)obj;
- (NSArray *)layoutManagers; 
  • 以下是一些重要、有用的的规格:
    • 如果集合没有顺序,返回NSSet比NSArray更好;
    • 如果在集合中插入元素,位置很重要的话,使用以下的格式比前面提到的更好:
例如: 
- (void)insertLayoutManager:(NSLayoutManager *)obj atIndex:(int)index; 
- (void)removeLayoutManagerAtIndex:(int)index; 
  • 以下一些实现细节要注意:
    • 这些方法通常暗含插入对象的拥有权(ownership)的管理,所以添加/插入元素的时候retain它们,移除的时候remove它们;
    • 如果插入对象想保持它原来的持有的对象,通常对该对象的setter方法不用retain,例如insertLayoutManager:atIndex: method方法。
    • NSLayoutManager类在以下的方法中同样这样处理:
- (void)setTextStorage:(NSTextStorage *)textStorage; 
- (NSTextStorage *)textStorage; 
通常你不用调用setTextStorage方法,但是你可能需要重写它。 
----(这段难理解,上面属于个人见解,参考原文:If the inserted objects need to have a pointer back to the main object, you do this (typically) with a set...method that sets the back pointer but does not retain. In the case of the insertLayoutManager:atIndex: method, the NSLayoutManager class does this in these methods: - (void)setTextStorage:(NSTextStorage *)textStorage; - (NSTextStorage *)textStorage; You would normally not call setTextStorage: directly, but might want to override it.) 以上说的集合方法的规则在NSWindow类中都有例子:
- (void)addChildWindow:(NSWindow *)childWin ordered:(NSWindowOrderingMode)place;
- (void)removeChildWindow:(NSWindow *)childWin;
- (NSArray *)childWindows;
- (NSWindow *)parentWindow;
- (void)setParentWindow:(NSWindow *)window;
7.1.8 私有方法
  • 在大多数情况下,私有的方法名称一般跟公共方法的名称都遵循同样的规则作为。然而,还有一个普遍的规则是给私有方法一个前缀,所以很容易区分他们跟公共方法。即使遵循这些规则,私有方法名称还是可以引起一些特殊问题。 当你你编写的Cocoa框架类的子类,你不知道你的私有方法是否无意中重写方法里同名称的私有方法。 在Cocoa框架中命名大多数私有的方法用下划线前缀开头(例如,_foodata),标记方法为私有。 对于这条规则,有两个建议:
    • 对于你自己的私有方法,不要使用下划线前缀。Apple约定了这条规则;
    • 如果是一个大cocoa框架类(如NSView)的子类,你要绝对确保你的私有的方法不同于父类的方法,您可以通过添加你自己独有的前缀来区分。前缀应尽可能的唯一的,也许是一个基于在你公司或项目的形式”xx_”。所以如果你的项目被称为Byte Flogger,前缀可以是BF_addobject; 虽然之前建议用前缀给私有方法命名,这看起来跟之前说的规则矛盾。但这块情况特殊,我们必须确保子类无意间重写父类的私有方法。
私有方法应该在实现文件中申明。
正例:
@interface ClassName(Private)

- (void)test;

@end

- (void)test
{
}
7.1.9 Blocks
  • Block是创建callback回调时首选的target-selector目标选择器模式,因为它使代码更易于阅读。Block里面的代码块应缩进4个空格。
  • 下面是一些相应的风格规则,取决于block有多长:
    • 如果block能一行写完,不要分行。
    • 如果有分行,右大括号必须与block声明的那一行的第一个字符对齐。
    • block内的代码必须缩进4个空格。
    • 如果block代码很多,比如,超过20行,建议移到外面用一个本地变量保存。
    • 如果block没有参数,在字符^{之间不要有空格,如果block有参数,字符^(之间不要有空格,但字符){之间有一个空格。
    • 包含内联block的调用可以让其代码段左对齐四空间缩进。这有助于理解包含多个内嵌块的调用。
    • 两个空间内缩进块也可以,但应该只用于保持与项目其他代码风格一致性时。
// The entire block fits on one line.
[operation setCompletionBlock:^{ [self onOperationDone]; }];

// The block can be put on a new line, indented four spaces, with the
// closing brace aligned with the first character of the line on which
// block was declared.
[operation setCompletionBlock:^{
    [self.delegate newDataAvailable];
}];

// Using a block with a C API follows the same alignment and spacing
// rules as with Objective-C.
dispatch_async(_fileIOQueue, ^{
    NSString* path = [self sessionFilePath];
    if (path) {
      // ...
    }
});

// An example where the parameter wraps and the block declaration fits
// on the same line. Note the spacing of |^(SessionWindow *window) {|
// compared to |^{| above.
[[SessionService sharedService]
    loadWindowWithCompletionBlock:^(SessionWindow *window) {
        if (window) {
          [self windowDidLoad:window];
        } else {
          [self errorLoadingWindow];
        }
    }];

// An example where the parameter wraps and the block declaration does
// not fit on the same line as the name.
[[SessionService sharedService]
    loadWindowWithCompletionBlock:
        ^(SessionWindow *window) {
            if (window) {
              [self windowDidLoad:window];
            } else {
              [self errorLoadingWindow];
            }
        }];
// Large blocks can be declared out-of-line.
void (^largeBlock)(void) = ^{
    // ...
};
[_operationQueue addOperationWithBlock:largeBlock];

// An example with multiple inlined blocks in one invocation.
[myObject doSomethingWith:arg1
    firstBlock:^(Foo *a) {
        // ...
    }
    secondBlock:^(Bar *b) {
        // ...
    }];
7.1.10 在init和dealloc中避免使用存取方法
  • 实例子类在init和dealloc方法执行的期间,可能会处于不确定的状态,所以应尽量避免在这些方法中使用存取方法。
  • 子类还没有被初始化或已经释放时,存取方法可能不可靠。无论何时,直接在方法中分配和释放变量,而不是依靠存取方法。
- (id)init {
      self = [super init];
      if (self) {
            _bar = [[NSMutableString alloc] init];  // good
  }
      return self;
}

- (void)dealloc {
      [_bar release];                           // good
      [super dealloc];
}
- (id)init {
      self = [super init];
  if (self) {
        self.bar = [NSMutableString string];  // avoid
     }
     return self;
}

- (void)dealloc {
  self.bar = nil;                         // avoid
  [super dealloc];
}
dealloc方法应该被放置在实现方法的顶部,直接在@synthesize@dynamic语句之后。init方法应该被放置在dealloc方法的下面。
Init方法
Init方法应该遵循Apple生成代码模板的命名规则,返回类型应该使用instancetype而不是id。
- (instancetype)init { 
self = [super init];  
 if (self) {   
    // ...    
 }   
 return self;   
}  
7.1.11 单例模式
  • 单例对象应该使用线程安全模式来创建共享实例。
+ (instancetype)sharedInstance {     
static id sharedInstance = nil;     
static dispatch_once_t onceToken;     
dispatch_once(&onceToken, ^{       
sharedInstance = [[self alloc] init];    
 });    
 return sharedInstance;  
 }   
这会防止possible and sometimes prolific crashes。
7.1.12 字面值
  • 对于NSString,NSDictionary,NSArray和NSNumber类,当需要创建这些类的不可变实例时,应该使用这些类的字面值表示形式。使用字面值表示的时候nil不需要传入NSArray和NSDictionary中作为字面值。这种语法兼容老的iOS版本,因此可以在iOS5或者更老的版本中使用它。
良好的风格:
NSArray *names = @[@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul"];
NSDictionary *productManagers = @{@"iPhone" : @"Kate", @"iPad" : @"Kamal", @"Mobile Web" : @"Bill"};
NSNumber *shouldUseLiterals = @YES;
NSNumber *buildingZIPCode = @10018;

不良的风格:
NSArray *names = [NSArray arrayWithObjects:@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul", nil];
NSDictionary *productManagers = [NSDictionary dictionaryWithObjectsAndKeys: @"Kate", @"iPhone", @"Kamal", @"iPad", @"Bill", @"Mobile Web", nil];
NSNumber *shouldUseLiterals = [NSNumber numberWithBool:YES];
NSNumber *buildingZIPCode = [NSNumber numberWithInteger:10018];
如非必要,避免使用特定类型的数字(相较于使用5.3f,应使用5.3)。

7.2 函数

7.2.1 函数命名
  • Objective-c中实现一个功能可以通过函数和方法。当你的对象是单实例或者处理一个子功能时候,更适合用函数。 函数命名有几下基本原则:

    • 函数名类似方法名,但有一些例外: 它们用你在类/常数中的前缀开头,并且前缀后的首字母大写。
    • 许多函数名字已动词开头,描述函数实现的功能:NSHighlightRect NSDeallocateObject
    • 函数如果是查询一些属性,命名有一些特别的规定:
      • 如果函数返回第一个参数的属性,省略动词:
        unsigned int NSEventMaskFromType(NSEventType type) float NSHeight(NSRect aRect)
  • 如果函数返回值是指针,使用Get:const char *NSGetSizeAndAlignment(const char *typePtr, unsigned int *sizep, unsigned int *alignp)

  • 如果函数返回值是布尔型,函数名用变化的(inflected)动词开头:BOOL NSDecimalIsNotANumber(const NSDecimal *decimal)

7.2.2 CGRect函数
  • 相较于使用结构体辅助函数(如CGRectMake()函数),优先使用C99结构体初始化语法。
  CGRect rect = {.origin.x = 3.0, .origin.y = 12.0, .size.width = 15.0, .size.height = 80.0 };
  • 当访问CGRect结构体的x、y、width、height成员时,应使用CGGeometry函数,不直接访问结构体成员。苹果对CGGeometry函数的介绍:
    All functions described in this reference that take CGRect data structures as inputs implicitly standardize those rectangles before calculating their results. For this reason, your applications should avoid directly reading and writing the data stored in the CGRect data structure. Instead, use the functions described here to manipulate rectangles and to retrieve their characteristics.
良好的风格:
CGRect frame = self.view.frame;
CGFloat x = CGRectGetMinX(frame);
CGFloat y = CGRectGetMinY(frame);
CGFloat width = CGRectGetWidth(frame);
CGFloat height = CGRectGetHeight(frame); 
不良的风格:
CGRect frame = self.view.frame;
CGFloat x = frame.origin.x;
CGFloat y = frame.origin.y;
CGFloat width = frame.size.width;
CGFloat height = frame.size.height;

8. 头文件

8.1 #import、#include与@class

  • 当包含一个使用Objective-C、Objective-C++的头文件时,使用#import。
  • 当包含一个使用标准C、C++头文件时,使用#include。头文件应该提供自己的。
  • 除了继承一个类或实现一个协议,否则在头文件中仅使用类声明@class指令,不用#import导入类头文件。
  • 说明:头文件中应避免包含其它不相关的头文件,一次头文件包含就相当于一次代码拷贝。

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

说明:正例:
@class SubClassName
@interface ClassName : NSObject
{
    SubClassName *m_pSubClassName;
}
反例:
#import “SubClassName.h”;
@interface ClassName : NSObject
{
    SubClassName *m_pSubClassName;
}

9. 可靠性

  • 为保证代码的可靠性,编程时请遵循如下基本原则,优先级递减:
    • 正确性,指程序要实现设计要求的功能。
    • 稳定性、安全性,指程序稳定、可靠、安全。
    • 可测试性,指程序要方便测试。
    • 规范/可读性,指程序书写风格、命名规则等要符合规范。
    • 全局效率,指软件系统的整体效率。
    • 局部效率,指某个模块/子模块/函数的本身效率。
    • 个人表达方式/个人方便性,指个人编程习惯。
    • *

9.1内存使用

9.1.1 防止内存操作越界。
  • 说明:内存操作主要是指对数组、指针、内存地址等的操作,内存操作越界是软件系统主要错误之一,后果往往非常严重,所以当我们进行这些操作时一定要仔细。
正例:
const int MAX_USE_NUM = 10                      // 用户号为1-10
unsigned char aucLoginFlg[MAX_USR_NUM + 1]={0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 
- (void)ArrayFunction
{
unsigned char ucUserNo;
for (ucUserNo = 0; ucUserNo < MAX_USE_NUM; ucUserNo++)
{
aucLoginFlg[ucUser_No] = ucUserNo;
… …
}
}
反例:
const int MAX_USE_NUM = 10                      // 用户号为1-10
unsigned char aucLoginFlg[MAX_USR_NUM]={0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 
- (void)ArrayFunction
{
unsigned char ucUserNo;
for (ucUserNo = 1; ucUserNo < 11; ucUserNo++)   // 10已经越界了
{
aucLoginFlg[User_No] = ucUserNo;
… …
}
}
9.1.2 必须对动态申请的内存做有效性检查,并进行初始化;动态内存的释放必须和分配成对以防止内存泄漏,释放后内存指针置为nil。
  • 说明:对嵌入式系统,通常内存是有限的,内存的申请可能会失败,如果不检查就对该指针进行操作,可能出现异常,而且这种异常不是每次都出现,比较难定位。
  • 指针释放后,该指针可能还是指向原有的内存块,可能不是,变成一个野指针,一般用户不会对它再操作,但用户失误情况下对它的操作可能导致程序崩溃。
正例:
- (void)memmoryFunction
{
unsigned char *pucBuffer = NULL;
pucBuffer = GetBuffer(sizeof(DWORD));   
if (NULL != pucBuffer)          // 申请的内存指针必须进行有效性验证
{
            // 申请的内存使用前必须进行初始化
memset(pucBuffer, 0xFF, sizeof(DWORD)); 
}
….
FreeBuffer(pucBuffer);      // 申请的内存使用完毕必须释放
pucBuffer = NULL;           // 申请的内存释放后指针置为空
…
}
  • 在哪申请在哪释放。
9.1.3 变量在使用前应初始化,防止未经初始化的变量被引用。
  • 说明:不同的编译系统,定义的变量在初始化前其值是不确定的。有些系统会初始化为0,而有些不是。
9.1.4 指针使用
  • 指针类型变量必须初始化为nil。
  • 指针不要进行复杂的逻辑或算术操作。
  • 说明:指针加一的偏移,通常由指针的类型确定,如果通过复杂的逻辑或算术操作,则指针的位置就很难确定。
  • 减少指针和数据类型的强制类型转化。
  • 全局指针释放后置为nil值。
9.1.5 对变量进行赋值时,必须对其值进行合法性检查,防止越界等现象发生。
  • 说明:尤其对全局变量赋值时,应进行合法性检查,以提高代码的可靠性、稳定性。
9.1.6 非初始化方法中的alloc操作之前必须要nil判断;。
9.1.7 类
  • 在编写派生类的赋值时,注意不要忘记对基类的成员变量重新赋值。
    • 说明:除非在派生类中调用基类的赋值函数,否则基类变量不会自动被赋值。
正例:
- (void)viewDidLoad 
{
    [super viewDidLoad];
}

10. 断言与错误处理

  • 断言是对某种假设条件进行检查(可理解为若条件成立则无动作,否则应报告)。它可以快速发现并定位软件问题,同时对系统错误进行自动报警。断言可以对在系统中隐藏很深,用其它手段极难发现的问题进行定位,从而缩短软件问题定位时间,提高系统的可测性。在实际应用时,可根据具体情况灵活地设计断言。
    10.1 整个软件系统应该采用统一的断言。如果系统不提供断言,则应该自己构造一个统一的断言供编程时使用。
  • 说明:整个软件系统提供一个统一的断言函数,如Assert(exp),同时可提供不同的宏进行定义(可根据具体情况灵活设计),如:
1#define ASSERT_EXIT_M    中断当前程序执行,打印中断发生的文件、行号,该宏一般在单调时使用。2#define ASSERT_CONTINUE_M    打印程序发生错误或异常的文件,行号,继续进行后续的操作,该宏一般在联调时使用。3#define ASSERT_OK_M  空操作,程序发生错误情况时,继续进行,可以通过适当的方式通知后台的监控或统计程序,该宏一般在RELEASE版本中使用。

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

说明:加快软件运行速度。

10.3 使用断言检查函数输入参数的有效性、合法性。

  • 说明:检查函数的输入参数是否合法,如输入参数为指针,则可用断言检查该指针是否为空,如输入参数为索引,则检查索引是否在值域范围内。
正例: 
BYTE  StoreCsrMsg(WORD wIndex,  T_CMServReq  *ptMsgCSR)
{
      WORD      wStoreIndex;
      T_FuncRet     tFuncRet;

      Assert (wIndex < MAX_DATA_AREA_NUM_A);  // 使用断言检查索引
      Assert (ptMsgCSR != NULL);                  // 使用断言检查指针// 其它代码

      return OK_M;
}
  • 10.4 错误处理
    当方法通过引用来返回一个错误参数,判断返回值而不是错误变量。
应该:
NSError *error;  
 if (![self trySomethingWithError:&error]) {
     // Handle Error   
}   
不应该:
NSError *error;  
 [self trySomethingWithError:&error];  
 if (error) {
     // Handle Error   
}  

在成功的情况下,有些Apple的APIs记录垃圾值(garbage values)到错误参数(如果non-NULL),那么判断错误值会导致false负值和crash。

10.5 异常

  • 每个@标签应该有独立的一行,在@与左大括号({)之间需要有一个空格。@catch与被捕捉到的异常对象的声明之间也要有一个空格。
  • 如果你必须使用Objective-C的异常,按下面的格式进行编码代码。然后,请参见Avoid Throwing Exceptions来了解不应该使用异常的原因。
@try {
  foo();
}
@catch (NSException *ex) {
  bar(ex);
}
@finally {
  baz();
}
  • 尽管你可以为了一些目的自由的使用异常(由NSException类和相关函数提供),Cocoa将编程中出现错误,例如数组越界,看做异常。Cocoa不使用异常去处理常规的、预料的错误情况,例如,返回值为nil、NULL、NO或一些错误代码。详细参考《Error Handling Programming Guide》。
    异常通过全局的字符串对象定义,格式如下:[Prefix] + [UniquePartOfName] + Exception; 其中unique part of the name是由单词组合而成,每个首字母大写。例如:
    NSColorListIOException
    NSColorListNotEditableException
    NSDraggingException
    NSFontUnavailableException

11. Xcode工程

  • 物理文件应该与Xcode工程文件保持同步来避免文件扩张。任何Xcode分组的创建应该在文件系统的文件体现。代码不仅是根据类型来分组,而且还可以根据功能来分组,这样代码更加清晰。
    尽可能在target的Build Settings打开”Treat Warnings as Errors,和启用以下additional warnings。如果你需要忽略特殊的警告,使用Clang’s pragma feature。
11.1 避免过多直接使用立即数。
正例:
ViewBounds.size.height = VIEW_BOUNDS_HEIGHT;
反例: 
ViewBounds.size.height = 150;
Height = 150;
11.2 addObject之前要非空判断。
11.3 release版本代码去掉NSLog打印,除了保留异常分支的NSLog。
11.4 禁止在代码中直接写死字符串资源,必须要用字符串ID替代。
  • 说明:应该要考虑多语言国际化,尽量使用NSLocalizedStringFromTable实现对字符串ID的引用
11.5 对于框架设计,逻辑层应尽量与UI层分离,降低耦合度。
11.6 同等难度下,优先考虑代码实现窗体创建。

说明:代码实现窗体创建容易移植,优先考虑代码实现来替代xib实现

11.7 通用的缩写和简称

通常你不用缩写你的命名,当你编写接口时候。参考基本命名规则章节。然而,下面所列举的缩写都是众所周知的,你可以继续使用它们。有以下几点需要注意:

  • 缩写的替代格式使用在标准C语音库中被允许。例如:“alloc” and “getc”。
  • 你可以在参数中更自由的使用缩写。例如:“imageRep”, “col” (for “column”),“obj”, and “otherWin”
    缩写
    意义
    alloc
    Allocate
    alt
    Alternate
    app
    应用程序,例, NSApp全局应用程序对象。
    “application” 全拼在delegate方法、通知中等
    calc
    Calculate.
    dealloc
    Deallocate.
    func
    Function.
    horiz
    Horizontal.
    info
    Information
    init
    Initialize
    max/min
    Maximum/Minimum.
    msg
    Message
    nib
    Interface Builder archive.
    pboard
    Pasteboard (but only in constants
    rect
    Rectangle.
    Rep
    Representation (used in class name such as NSBitmapImageRep
    temp
    Temporary.
    vert
    Vertical.
    你也可以使用一些计算机行业通用的缩写,例:ASCII、PDF、XML、HTML、URL、RTF、HTTP、TIFF、JPG、PNG、GIF、LZW、ROM、RGB、CMYK、MIDI、FTP

你可能感兴趣的:(IOS,ios,ios开发,iOS代码规范,oc代码规范,移动开发)