说明:这是软件开发的基本要点,软件的生命周期贯穿产品的开发、测试、生产、用户使用、版本升级和后期维护等长期过程,只有易读、易维护的软件代码才具有生命力。
说明:简单是最美。保持代码的简单化是软件工程化的基本要求。不要过分追求技巧,否则会降低程序的可读性。
说明:编程首先考虑的是满足正确性、健壮性、可维护性、可移植性等质量因素,最后才考虑程序的效率和资源占用。
说明:不可以测试的代码是无法保障质量的,开发人员要牢记这一点来设计、编码。实现设计功能的同时,要提供可以测试、验证的方法。
说明:方法是一个处理单元,是有特定功能的,所以应该很好地规划方法,不能是所有东西都放在一个方法里实现
说明:应该使用US英语。
应该:
UIColor *myColor = [UIColor whiteColor];
不应该:
UIColor *myColour = [UIColor whiteColor];
说明:以下内容如果某些节不需要,可以忽略。但是其它节要保持该次序。
头文件布局:
文件头(参见“注释”一节)
#import (依次为标准库头文件、非标准库头文件)
全局宏
常量定义
全局数据类型
类定义
正例:
/********************************** 文件引用 *******************************/
/********************************** 类引用 ********************************/
/********************************** 宏定义 ********************************/
/*********************************** 常量 ********************************/
/********************************* 类型定义 *******************************/
/********************************** 类定义 ********************************/
/********************************* 文件引用 *******************************/
/********************************* 文件引用 *******************************/
说明:以下内容如果某些节不需要,可以忽略。但是其它节要保持该次序。
实现文件布局:
文件头(参见“注释”一节)
#import (依次为标准库头文件、非标准库头文件)
文件内部使用的宏
常量定义
文件内部使用的数据类型
全局变量
本地变量(即静态全局变量)
类的实现
正例:
/********************************** 文件引用 *******************************/
/********************************** 宏定义 ********************************/
/********************************** 常量 *********************************/
/********************************* 类型定义 ******************************/
/********************************* 全局变量 ******************************/
/********************************** 原型 ********************************/
/********************************* 类特性 *******************************/
@implementation ClassName
@synthesize variableName;
/********************************* 类的实现 *****************************/
正例:
#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 {}
说明:这样可以防止书写失误,也易于阅读。
正例:
if (varible1 < varible2)
{
varible1 = varible2;
}
反例:下面的代码执行语句紧跟if的条件之后,而且没有加{},违反规则。
if (varible1 < varible2) varible1 = varible2;
不使用上面的,使用K&R风格 (习惯这种)
if (user.isHappy) {
//Do something
} else {
//Do something
}
正例:
float *pfBuffer;
反例:
float* pfBuffer;
说明:这样便于程序阅读和查找。
正例:
iLength = 10;
iWidth = 5; // 矩形的长与宽关系较密切,放在一起。
StrCaption = “Test”;
反例:
iLength = 10;
strCaption = “Test”;
iWidth = 5;
大括号在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;
}
当需要提高代码的清晰性和简洁性时,三元操作符?:才会使用。单个条件求值常常需要它。多个条件求值时,如果使用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;
表达式是语句的一部分,它们是不可分割的。表达式和语句虽然看起来比较简单,但使用时隐患比较多。归纳了正确使用表达式和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] ;
正例:
设bFlag 是布尔类型的变量
if (bFlag) // 表示flag为真
if (!bFlag) // 表示flag为假
反例:
设bFlag 是布尔类型的变量
if (bFlag == TRUE)
if (bFlag == 1)
if (bFlag == FALSE)
if (bFlag == 0)
正例:
if (iValue == 0)
if (iValue != 0)
反例:
if (iValue) // 会让人误解 iValue是布尔变量
if (!iValue)
正例:
if ((fResult >= -EPSINON) && (fResult <= EPSINON))
反例:
if (fResult == 0.0) // 隐含错误的比较
其中EPSINON是允许的误差(即精度)。
正例:
if (pHead == nil) // pHead与NULL显式比较,强调pHead是指针变量
if (pHead != nil)
反例:
if (pHead == 0) // 容易让人误解pHead是整型变量
if (pHead != 0)
或者
if (pHead) // 容易让人误解pHead是布尔变量
if (!pHead)
正例:
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);
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();
}
}
正例:
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]);
}
相比之下,正例的写法更加直观,尽管两者的功能是相同的。
应该:
- (void)someMethod {
if (![someOther boolValue]) {
return;
}
//Do something important
}
不应该:
- (void)someMethod {
if ([someOther boolValue]) {
//Do something important
}
}
正例:
int aiNumbers[4][3] =
{
1, 1, 1,
2, 4, 8,
3, 9, 27,
4, 16, 64
}
正例:
tPDBRes.wHead = 0;
tPDBRes.wTail = wMaxNumOfPDB - 1;
tPDBRes.wFree = wMaxNumOfPDB;
tPDBRes.wAddress = wPDBAddr;
tPDBRes.wSize = wPDBSize;
说明:空行起着分隔程序段落的作用。适当的空行可以使程序的布局更加清晰。
正例:
(void)hey
{
[hey实现代码]
}
// 空一行
// 空一行
(void)ack
{
[ack实现代码]
}
反例:
void Foo::Hey(void)
{
[Hey实现代码]
}
void Foo::Ack(void)
{
[Ack实现代码]
}
// 两个函数的实现是两个逻辑程序块,应该用空行加以分隔。
一元操作符如“!”、“~”、“++”、“–”、“*”、“&”(地址运算符)等前后不加空格。“[]”、“.”、“->”这类操作符前后不加空格。
正例:
!bValue
~iValue
++iCount
*strSource
&fSum
aiNumber[i] = 5;
tBox.dWidth
tBox->dWidth
NewType a = (NewType)b;
正例:
fValue = fOldValue;
fTotal + fValue
iNumber += 2;
说明:if、for、while等关键字之后应留一个空格再跟左括号‘(’,以突出关键字。
说明:函数名后紧跟左括号‘(’,以与关键字区别。
说明:方法名后紧跟’:’,然后紧跟形参, 返回类型’(‘与’-‘之间有一个空格。
正例:
-凵(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations.
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
‘(’向后紧跟,‘)’、‘,’、‘;’向前紧跟,紧跟处不留空格。‘,’之后要留空格。‘;’不是行结束符号时其后要留空格
正例:
例子中的 凵 代表空格。
for凵(i凵=凵0;凵i凵<凵MAX_BSC_NUM;凵i++)
{
DoSomething(iWidth,凵iHeight);
}
正例:
/* 注释内容 */
// 注释内容
反例:
/注释内容/
//注释内容
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进行缩进设置的截图。
如果一个方法内有多个功能区域,可以使用空行分隔功能区域;
正例:
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]);
正例:
extern double FAR CalcArea(double dWidth, double dHeight);
反例:
extern double FAR
CalcArea(double dWidth, double dHeight);
正例:
下面是文件头部的中文注释:
/*************************************************************
* 版权所有 (C)2011中兴软件技术(南昌)有限公司
*
* 文件名称: // 文件名
* 文件标识: // 见配置管理计划书
* 内容摘要: // 简要描述本文件的内容,包括主要模块、函数及其功能的说明
* 其它说明: // 其它内容的说明
* 当前版本: // 输入当前版本
* 作 者: // 输入作者名字及单位
* 完成日期: // 输入完成日期,例:2011年11月29日
* 修改记录1:// 修改历史记录,包括修改日期、修改者及修改内容
* 修改日期:
* 版 本 号://或版本号
* 修 改 人:
* 修改内容://修改原因以及修改内容说明
* 修改记录2:…
**************************************************************/
3.2 方法头部应进行注释,列出:函数的目的/功能、输入参数、输出参数、返回值、访问和修改的表、修改信息等,除了函数(方法)名称和功能描述必须描述外,其它部分建议写描述。
说明:注释必须列出:函数名称、功能描述、输入参数、输出参数、返 回 值、修改信息等。备注:方法名称、功能描述要正确描述。
正例:
/*************************************************************
* 方法名称: // 方法名称
* 功能描述: // 方法功能、性能等的描述
* 输入参数: // 输入参数说明,包括每个参数的作用、取值说明及参数间关系
* 输出参数: // 对输出参数的说明。
* 返 回 值: // 方法返回值的说明
* 其它说明: // 其它说明
**************************************************************/
说明:在使用缩写时或之前,应对缩写进行必要的说明。
正例:
如下书写比较结构清晰
/* 获得子系统索引 */
iSubSysIndex = aData[iIndex].iSysIndex;
/* 代码段1注释 */
[ 代码段1 ]
/* 代码段2注释 */
[ 代码段2 ]
反例1:
如下例子注释与描述的代码相隔太远。
/* 获得子系统索引 */
iSubSysIndex = aData[iIndex].iSysIndex;
反例2:
如下例子注释不应放在所描述的代码下面。
iSubSysIndex = aData[iIndex].iSysIndex;
/* 获得子系统索引 */
反例3:
如下例子,显得代码与注释过于紧凑。
/* 代码段1注释 */
[ 代码段1 ]
/* 代码段2注释 */
[ 代码段2 ]
正例:
/*
* 变量作用说明
* 变量值说明
*/
BYTE g_ucTranErrorCode;
3.5 注释与所描述内容进行同样的缩排。
说明:可使程序排版整齐,并方便注释的阅读与理解。
正例:
如下注释结构比较清晰
- (int)doSomething
{
/* 代码段1注释 */
[ 代码段1 ]
/* 代码段2注释 */
[ 代码段2 ]
}
反例:
如下例子,排版不整齐,阅读不方便;
int DoSomething(void)
{
/* 代码段1注释 */
[ 代码段1 ]
/* 代码段2注释 */
[ 代码段2 ]
}
好的命名规则能极大地增加可读性和可维护性。同时,对于一个有上百个人共同完成的大项目来说,统一命名约定也是一项必不可少的内容。本章对程序中的所有标识符(包括变量名、常量名、函数名、类名、结构名、宏定义等)的命名做出约定。
说明:尽管局部变量和全局变量的作用域不同而不会发生语法错误,但容易使人误解。
说明:变量活动范围前缀规范如下(少用全局变量):
g_ : 全局变量
s_ : 模块内静态变量
空 : 局部变量不加范围前缀
说明:方法名力求清晰、明了,通过方法名就能够判断方法的主要功能。方法名中不同意义字段之间不要用下划线连接,而要把每个字段的首字母大写以示区分。方法命名采用大小写字母结合的形式,但专有名词不受限制。
说明:参考匈牙利命名规则,常见的简写如下:
整型 n
指针 p
字符串 str
布尔 b
字符 c
函数 fn
正例:
@interface CMainMenu
{
int m_nWidth;
NString *m_strName;
BOOL m_bCheck;
}
建议1 尽量避免名字中出现数字编号,如Value1、Value2等,除非逻辑上的确需要编号。
建议2标识符前最好不加项目、产品、部门的标识。
说明:这样做的目的是为了代码的可重用性。
良好的风格:
RefreshBarButtonItem / RefreshBarButtonItem@2x
和 RefreshBarButtonItemSelected / RefreshBarButtonItemSelected@2x
ArticleNavigationBarWhite / ArticleNavigationBarWhite@2x
和 ArticleNavigationBarBlackSelected /
ArticleNavigationBarBlackSelected@2x.
被用作相似用途的图片应该使用一个图片文件夹进行分开管理。
头文件
声明
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文件的命名规则暴露给测试。
如果属性是名称/动词意思,
格式是:@property (…) type nounOrVerb
例:@property (strong) NSString *title;
@property (assign) BOOL isShowsAlpha;
例如:@property (assign, getter=isEditable) BOOL editable;
例:
@implementation MyClass {
BOOL _showsTitle;
}
应该:
@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除非你需要原子操作。
私有属性应该被声明在实现文件的类扩展中(即匿名的category)。不要将私有属性声明在命名的category(如RNCPrivate或private),除非是扩展其他类。
@public与@private
@public以及@private访问标识符应该以一个空格缩进。
与C++中的public, private,以及protected非常相似。
@interface MyClass : NSObject {
@public
...
@private
...
}
@end
NSString应该使用copy而不是strong的属性特性。
为什么?即使你声明一个NSString的属性,有人可能传入一个NSMutableString的实例,然后在你没有注意的情况下修改它。
应该:
@property (copy, nonatomic) NSString *tutorialName;
不应该:
@property (strong, nonatomic) NSString *tutorialName;
良好的风格:
view.backgroundColor = [UIColor orangeColor];
[UIApplication sharedApplication].delegate;
不良的风格:
[view setBackgroundColor:[UIColor orangeColor]];
UIApplication.sharedApplication.delegate;
一个变量只用来表示一个特定功能,不能把一个变量作多种用途,即同一变量取值不同时,其代表的意义也不同。最好不要在语句块内声明局部变量。
循环语句只完成循环控制功能,if语句只完成逻辑判断功能,不能完成计算赋值功能。
正例:
do
{
[处理语句]
cInput = GetChar();
} while (cInput == 0);
反例:
do
{
[处理语句]
} while (cInput = GetChar());
说明:可以避免访问全局变量时引起的错误。
正例:
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;
}
良好的风格:
NSString *accountName;
NSMutableArray *mailboxes;
NSArray *defaultHeaders;
BOOL userInputWasUpdated;
不良的风格:
NSString *accountNameString;
NSMutableArray *mailboxArray;
NSArray *defaultHeadersArray;
BOOL userInputWasUpdatedBOOL;
NSImage *previewPaneImage;
NSProgressIndicator *uploadIndicator;
NSFontManager *fontManager; // 基于类名命名
良好的风格:
NSDictionary * keyedAccountNames;
NSDictionary * messageDictionary;
NSIndexSet * selectedMailboxesIndexSet;
是容易重复被使用和无需通过查找和代替就能快速修改值。常量应该使用static来声明而不是使用#define,除非显式地使用宏。
//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。
例:
枚举应该使用下面这种方式
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;
如果集合内容只有一行,开始中括号[之后和结束中括号]之前需要一个空格。
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",
};
说明:在宏定义中,对表达式和变量使用括号,可以避免可能发生的计算错误。
正例:
#define HANDLE(A, B) (( A ) / ( B ))
反例:
#define HANDLE(A, B) (A / B)
正例:
#define BUTTON_WIDTH (int)320
反例:
#define kButtonWidth (int)320
说明:不同的编译器,默认类型不一样。
正例:
#define BUTTON_WIDTH (int)320
反例:
#define BUTTON_WIDTH 320
设计结构时应力争使结构代表一种现实事务的抽象,而不是同时代表多种。结构中的各元素应代表同一事务的不同侧面,而不应把描述没有关系或关系很弱的不同事务的元素放到同一结构中。
正例:
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;
合理排列结构中元素顺序,可节省空间并增加可理解性。
正例:如下形式,不仅可节省字节空间,可读性也变好了。
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;
通知通过全局的字符串对象定义,格式如下:[Name of associated class] + [Did | Will] + [UniquePartOfName] + Notification;
例:
NSApplicationDidBecomeActiveNotification
NSWindowDidMiniaturizeNotification
NSTextViewDidChangeSelectionNotification
NSColorPanelColorDidChangeNotification
正例:
- (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;
-或者+与返回类型之间,需要有空格。参数列表中,只有参数之间有空格。
方法应该像这样:
- (void)doSomethingWithString:(NSString *)theString {
...
}
- (void)doSomethingWith:(GTMFoo *)theFoo
rect:(NSRect)theRect
interval:(float)theInterval {
...
}
- (void)short:(GTMFoo *)theFoo
longKeyword:(NSRect)theRect
evenLongerKeyword:(float)theInterval
error:(NSError **)theError {
...
}
方法定义的格式与方法声明的格式非常相似。当格式的风格有多种选择时,新的代码要与已经存在的代码保持一致。
方法调用时,所有参数应该在同一行。
[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个空格缩进左对齐
一般来讲,以下的一些方法中的关键词通常跟固定的参数搭配:
..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
良好的风格:
- (void)setTitle:(NSString *) aTitle;
- (void)setName:(NSString *)newName;
- (id)keyForOption:(CDCOption *) anOption
- (NSArray *) emailsForMailbox:(CDCMailbox *) theMailbox;
- (CDCEmail *) emailForRecipients:(NSArray *) theRecipients;
- (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
- (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 // 一个正在发生的动作
- (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
- (void)setCanHide:(BOOL)flag;
Y
- (BOOL)canHide;
Y
- (void)setShouldCloseDocument:(BOOL)flag;
Y
- (BOOL)shouldCloseDocument;
Y
- (void)setDoesAcceptGlyphInfo:(BOOL)flag;
N
- (BOOL)doesAcceptGlyphInfo;
N
- (void)getLineDash:(float *)pattern count:(int *)count phase:(float *)phase;
注意,这种形式的方法,其中的引用型参数应该能接收NULL,因为方法调用者可能并不需要多个返回值。
代理方法是那些当发生特定事件对象使用它delegate调用的方法(如果delegate实现了它),它们有着特定的格式,这些格式也适用于对象的datesource方法。
例如:- (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(int)row; - (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename;
类名省略了它的前缀并且小写开头。
例:- (BOOL)applicationOpenUntitledFile:(NSApplication *)sender;
例:- (void)windowDidChangeScreen:(NSNotification *)notification;
例:- (void)browserDidScroll:(NSBrowser *)sender;
例:- (BOOL)windowShouldClose:(id)sender;
例如:
@interface MyProtocoledClass : NSObject<NSWindowDelegate> {
@private
id _delegate;
}
- (void)setDelegate:(id)aDelegate;
@end
- (void)addElement:(elementType)anObj;
- (void)removeElement:(elementType)anObj;
- (NSArray *)elements;
例如:
- (void)addLayoutManager:(NSLayoutManager *)obj;
- (void)removeLayoutManager:(NSLayoutManager *)obj;
- (NSArray *)layoutManagers;
例如:
- (void)insertLayoutManager:(NSLayoutManager *)obj atIndex:(int)index;
- (void)removeLayoutManagerAtIndex:(int)index;
- (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;
私有方法应该在实现文件中申明。
正例:
@interface ClassName(Private)
- (void)test;
@end
- (void)test
{
}
// 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) {
// ...
}];
- (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;
}
+ (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。
良好的风格:
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)。
Objective-c中实现一个功能可以通过函数和方法。当你的对象是单实例或者处理一个子功能时候,更适合用函数。 函数命名有几下基本原则:
如果函数返回值是指针,使用Get:const char *NSGetSizeAndAlignment(const char *typePtr, unsigned int *sizep, unsigned int *alignp)
如果函数返回值是布尔型,函数名用变化的(inflected)动词开头:BOOL NSDecimalIsNotANumber(const NSDecimal *decimal)
CGRect rect = {.origin.x = 3.0, .origin.y = 12.0, .size.width = 15.0, .size.height = 80.0 };
良好的风格:
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;
说明:正例:
@class SubClassName;
@interface ClassName : NSObject
{
SubClassName *m_pSubClassName;
}
反例:
#import “SubClassName.h”;
@interface ClassName : NSObject
{
SubClassName *m_pSubClassName;
}
正例:
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;
… …
}
}
正例:
- (void)memmoryFunction
{
unsigned char *pucBuffer = NULL;
pucBuffer = GetBuffer(sizeof(DWORD));
if (NULL != pucBuffer) // 申请的内存指针必须进行有效性验证
{
// 申请的内存使用前必须进行初始化
memset(pucBuffer, 0xFF, sizeof(DWORD));
}
….
FreeBuffer(pucBuffer); // 申请的内存使用完毕必须释放
pucBuffer = NULL; // 申请的内存释放后指针置为空
…
}
正例:
- (void)viewDidLoad
{
[super viewDidLoad];
}
(1)#define ASSERT_EXIT_M 中断当前程序执行,打印中断发生的文件、行号,该宏一般在单调时使用。
(2)#define ASSERT_CONTINUE_M 打印程序发生错误或异常的文件,行号,继续进行后续的操作,该宏一般在联调时使用。
(3)#define ASSERT_OK_M 空操作,程序发生错误情况时,继续进行,可以通过适当的方式通知后台的监控或统计程序,该宏一般在RELEASE版本中使用。
说明:加快软件运行速度。
正例:
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;
}
应该:
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。
@try {
foo();
}
@catch (NSException *ex) {
bar(ex);
}
@finally {
baz();
}
正例:
ViewBounds.size.height = VIEW_BOUNDS_HEIGHT;
反例:
ViewBounds.size.height = 150;
Height = 150;
说明:代码实现窗体创建容易移植,优先考虑代码实现来替代xib实现
通常你不用缩写你的命名,当你编写接口时候。参考基本命名规则章节。然而,下面所列举的缩写都是众所周知的,你可以继续使用它们。有以下几点需要注意: