Objective-c编程语言规范

代码格式
git

使用空格而不是制表符 Tabgithub

不要在工程里使用 Tab 键,使用空格来进行缩进。在 Xcode > Preferences > Text Editing Tab 和自动缩进都设置为 4 个空格。(Google 的标准是使用两个空格来缩进,但这里仍是推荐使用 Xcode 默认的设置。)编程

每一行的最大长度安全

一样的,在 Xcode > Preferences > Text Editing > Page guide at column: 中将最大行长设置为 80 ,过长的一行代码将会致使可读性问题。session

函数的书写数据结构

一个典型的 Objective-C 函数应该是这样的:闭包

- (void)writeVideoFrameWithData:(NSData *)frameData timeStamp:(int)timeStamp {app

...框架

}less

- (void) 之间应该有一个空格,第一个大括号 { 的位置在函数所在行的末尾,一样应该有一个空格。(我司的 C 语言规范要求是第一个大括号单独占一行,但考虑到 OC 较长的函数名和苹果 SDK 代码的风格,仍是将大括号放在行末。)

若是一个函数有特别多的参数或者名称很长,应该将其按照 : 来对齐分行显示:

-(id)initWithModel:(IPCModle)model

ConnectType:(IPCConnectType)connectType

Resolution:(IPCResolution)resolution

AuthName:(NSString *)authName

Password:(NSString *)password

MAC:(NSString *)mac

AzIp:(NSString *)az_ip

AzDns:(NSString *)az_dns

Token:(NSString *)token

Email:(NSString *)email

Delegate:(id)delegate;

在分行时,若是第一段名称太短,后续名称能够以 Tab 的长度( 4 个空格)为单位进行缩进:

- (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];

// 第一段名称太短的话后续能够进行缩进

[myObj short:arg1

longKeyword:arg2

evenLongerKeyword:arg3

error:arg4];

如下写法是错误的:

// 错误,要么写在一行,要么所有分行

[myObject doFooWith:arg1 name:arg2

error:arg3];

[myObject doFooWith:arg1

name:arg2 error:arg3];

// 错误,按照 ':' 来对齐,而不是关键字

[myObject doFooWith:arg1

name:arg2

error:arg3];

@public @private 标记符

@public @private 标记符应该以一个空格来进行缩进:

@interface MyClass : NSObject {

@public

...

@private

...

}

@end

协议( Protocols

在书写协议的时候注意用 <> 括起来的协议和类型名之间是没有空格的,好比 IPCConnectHandler(), 这个规则适用全部书写协议的地方,包括函数声明、类声明、实例变量等等:

@interface MyProtocoledClass : NSObject {

@private

id _delegate;

}

- (void)setDelegate:(id)aDelegate;

@end

闭包( Blocks

根据 block 的长度,有不一样的书写规则:

  • 较短的 block 能够写在一行内。

  • 若是分行显示的话, block 的右括号 } 应该和调用 block 那行代码的第一个非空字符对齐。

  • block 内的代码采用 4 个空格 的缩进。

  • 若是 block 过于庞大,应该单独声明成一个变量来使用。

  • ^ ( 之间, ^ { 之间都没有空格,参数列表的右括号 ) { 之间有一个空格。

// 较短的 block 写在一行内

[operation setCompletionBlock:^{ [self onOperationDone]; }];

// 分行书写的 block ,内部使用 4 空格缩进

[operation setCompletionBlock:^{

[self.delegate newDataAvailable];

}];

// 使用 C 语言 API 调用的 block 遵循一样的书写规则

dispatch_async(_fileIOQueue, ^{

NSString* path = [self sessionFilePath];

if (path) {

// ...

}

});

// 较长的 block 关键字能够缩进后在新行书写,注意 block 的右括号 '}' 和调用 block 那行代码的第一个非空字符对齐

[[SessionService sharedService]

loadWindowWithCompletionBlock:^(SessionWindow *window) {

if (window) {

[self windowDidLoad:window];

} else {

[self errorLoadingWindow];

}

}];

// 较长的 block 参数列表一样能够缩进后在新行书写

[[SessionService sharedService]

loadWindowWithCompletionBlock:

^(SessionWindow *window) {

if (window) {

[self windowDidLoad:window];

} else {

[self errorLoadingWindow];

}

}];

// 庞大的 block 应该单独定义成变量使用

void (^largeBlock)(void) = ^{

// ...

};

[_operationQueue addOperationWithBlock:largeBlock];

// 在一个调用中使用多个 block ,注意到他们不是像函数那样经过 ':' 对齐的,而是同时进行了 4 个空格的缩进

[myObject doSomethingWith:arg1

firstBlock:^(Foo *a) {

// ...

}

secondBlock:^(Bar *b) {

// ...

}];

数据结构的语法糖

应该使用可读性更好的语法糖来构造 NSArray NSDictionary 等数据结构,避免使用冗长的 alloc,init 方法。

若是构造代码写在一行,须要在括号两端留有一个空格,使得被构造的元素于与构造语法区分开来:

// 正确,在语法糖的 "[]" 或者 "{}" 两端留有空格

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

};

构造字典时,字典的 Key Value 与中间的冒号 : 都要留有一个空格,多行书写时,也能够将 Value 对齐:

// 正确,冒号 ':' 先后留有一个空格

NSDictionary *option1 = @{

NSFontAttributeName : [NSFont fontWithName:@"Helvetica-Bold" size:12],

NSForegroundColorAttributeName : fontColor

};

// 正确,按照 Value 来对齐

NSDictionary *option2 = @{

NSFontAttributeName : [NSFont fontWithName:@"Arial" size:12],

NSForegroundColorAttributeName : fontColor

};

// 错误,冒号前应该有一个空格

NSDictionary *wrong = @{

AKey: @"b",

BLongerKey: @"c",

};

// 错误,每个元素要么单独成为一行,要么所有写在一行内

NSDictionary *alsoWrong= @{ AKey : @"a",

BLongerKey : @"b" };

// 错误,在冒号前只能有一个空格,冒号后才能够考虑按照 Value 对齐

NSDictionary *stillWrong = @{

AKey : @"b",

BLongerKey : @"c",

};

命名规范

基本原则

清晰

命名应该尽量的清晰和简洁,但在 Objective-C 中,清晰比简洁更重要。因为 Xcode 强大的自动补全功能,咱们没必要担忧名称过长的问题。

// 清晰

insertObject:atIndex:

// 不清晰, insert 的对象类型和 at 的位置属性没有说明

insert:at:

// 清晰

removeObjectAtIndex:

// 不清晰, remove 的对象类型没有说明,参数的做用没有说明

remove:

不要使用单词的简写,拼写出完整的单词:

// 清晰

destinationSelection

setBackgroundColor:

// 不清晰,不要使用简写

destSel

setBkgdColor:

然而,有部分单词简写在 Objective-C 编码过程当中是很是经常使用的,以致于成为了一种规范,这些简写能够在代码中直接使用,下面列举了部分:

alloc == Allocate max == Maximum

alt == Alternate min == Minimum

app == Application msg == Message

calc == Calculate nib == Interface Builder archive

dealloc == Deallocate pboard == Pasteboard

func == Function rect == Rectangle

horiz == Horizontal Rep == Representation (used in class name such as NSBitmapImageRep).

info == Information temp == Temporary

init == Initialize vert == Vertical

int == Integer

命名方法或者函数时要避免歧义

// 有歧义,是返回 sendPort 仍是 send 一个 Port 

sendPort

// 有歧义,是返回一个名字属性的值仍是 display 一个 name 的动做?

displayName

一致性

整个工程的命名风格要保持一致性,最好和苹果 SDK 的代码保持统一。不一样类中完成类似功能的方法应该叫同样的名字,好比咱们老是用 count 来返回集合的个数,不能在 A 类中使用 count 而在 B 类中使用 getNumber

使用前缀

若是代码须要打包成 Framework 给别的工程使用,或者工程项目很是庞大,须要拆分红不一样的模块,使用命名前缀是很是有用的。

  • 前缀由大写的字母缩写组成,好比 Cocoa NS 前缀表明 Founation 框架中的类, IB 则表明 Interface Builder 框架。

  • 能够在为类、协议、函数、常量以及 typedef 宏命名的时候使用前缀,但注意不要为成员变量或者方法使用前缀,由于他们自己就包含在类的命名空间内。

  • 命名前缀的时候不要和苹果 SDK 框架冲突。

命名类和协议( Class&Protocol

类名以大写字母开头,应该包含一个名词来表示它表明的对象类型,同时能够加上必要的前缀,好比 NSString,NSDate,NSScanner,NSApplication 等等。

而协议名称应该清晰地表示它所执行的行为,并且要和类名区别开来,因此一般使用 ing 词尾来命名一个协议,好比 NSCopying,NSLocking

有些协议自己包含了不少不相关的功能,主要用来为某一特定类服务,这时候能够直接用类名来命名这个协议,好比 NSObject 协议,它包含了 id 对象在生存周期内的一系列方法。

命名头文件( Headers

源码的头文件名应该清晰地暗示它的功能和包含的内容:

  • 若是头文件内只定义了单个类或者协议,直接用类名或者协议名来命名头文件,好比 NSLocale.h 定义了 NSLocale 类。

  • 若是头文件内定义了一系列的类、协议、类别,使用其中最主要的类名来命名头文件,好比 NSString.h 定义了 NSString NSMutableString

  • 每个 Framework 都应该有一个和框架同名的头文件,包含了框架中全部公共类头文件的引用,好比 Foundation.h

  • Framework 中有时候会实如今别的框架中类的类别扩展,这样的文件一般使用被扩展的框架名 +Additions 的方式来命名,好比 NSBundleAdditions.h

命名方法( Methods

Objective-C 的方法名一般都比较长,这是为了让程序有更好地可读性,按苹果的说法好的方法名应当能够以一个句子的形式朗读出来

方法通常以小写字母打头,每个后续的单词首字母大写,方法名中不该该有标点符号(包括下划线),有两个例外:

  • 能够用一些通用的大写字母缩写打头方法,好比 PDF,TIFF 等。

  • 能够用带下划线的前缀来命名私有方法或者类别中的方法。

若是方法表示让对象执行一个动做,使用动词打头来命名,注意不要使用 do does 这种多余的关键字,动词自己的暗示就足够了:

// 动词打头的方法表示让对象执行一个动做

- (void)invokeWithTarget:(id)target;

- (void)selectTabViewItem:(NSTabViewItem *)tabViewItem;

若是方法是为了获取对象的一个属性值,直接用属性名称来命名这个方法,注意不要添加 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;

不要用 and 来链接两个参数,一般 and 用来表示方法执行了两个相对独立的操做(从设计上来讲,这时候应该拆分红两个独立的方法):

// 错误,不要使用 "and" 来链接参数

- (int)runModalForDirectory:(NSString *)path andFile:(NSString *)name andTypes:(NSArray *)fileTypes;

// 正确,使用 "and" 来表示两个相对独立的操做

- (BOOL)openFile:(NSString *)fullPath withApplication:(NSString *)appName andDeactivate:(BOOL)flag;

方法的参数命名也有一些须要注意的地方 :

  • 和方法名相似,参数的第一个字母小写,后面的每个单词首字母大写

  • 不要再方法名中使用相似 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

存取方法( Accessor Methods

存取方法是指用来获取和设置类属性值的方法,属性的不一样类型,对应着不一样的存取方法规范:

// 属性是一个名词时的存取方法范式

- (type)noun;

- (void)setNoun:(type)aNoun;

// 栗子

- (NSString *)title;

- (void)setTitle:(NSString *)aTitle;

// 属性是一个形容词时存取方法的范式

- (NSString *)title;

- (void)setTitle:(NSString *)aTitle;

// 栗子

- (BOOL)isAdjective;

- (void)setAdjective:(BOOL)flag;

// 属性是一个动词时存取方法的范式

- (BOOL)verbObject;

- (void)setVerbObject:(BOOL)flag;

// 栗子

- (BOOL)showsAlpha;

- (void)setShowsAlpha:(BOOL)flag;

命名存取方法时不要将动词转化为被动形式来使用:

// 正确

- (void)setAcceptsGlyphInfo:(BOOL)flag;

- (BOOL)acceptsGlyphInfo;

// 错误,不要使用动词的被动形式

- (void)setGlyphInfoAccepted:(BOOL)flag;

- (BOOL)glyphInfoAccepted;

可使用 can,should,will 等词来协助表达存取方法的意思,但不要使用 do, does

// 正确

- (void)setCanHide:(BOOL)flag;

- (BOOL)canHide;

- (void)setShouldCloseDocument:(BOOL)flag;

- (BOOL)shouldCloseDocument;

// 错误,不要使用 "do" 或者 "does"

- (void)setDoesAcceptGlyphInfo:(BOOL)flag;

- (BOOL)doesAcceptGlyphInfo;

为何 Objective-C 中不适用 get 前缀来表示属性获取方法?由于 get Objective-C 中一般只用来表示从函数指针返回值的函数:

// 三个参数都是做为函数的返回值来使用的,这样的函数名可使用 "get" 前缀

- (void)getLineDash:(float *)pattern count:(int *)count phase:(float *)phase;

命名委托( Delegate

当特定的事件发生时,对象会触发它注册的委托方法。委托是 Objective-C 中经常使用的传递消息的方式。委托有它固定的命名范式。

一个委托方法的第一个参数是触发它的对象,第一个关键词是触发对象的类名,除非委托方法只有一个名为 sender 的参数:

// 第一个关键词为触发委托的类名

- (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(int)row;

- (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename;

// 当只有一个 "sender" 参数时能够省略类名

- (BOOL)applicationOpenUntitledFile:(NSApplication *)sender;

根据委托方法触发的时机和目的,使用 should,will,did 等关键词

- (void)browserDidScroll:(NSBrowser *)sender;

- (NSUndoManager *)windowWillReturnUndoManager:(NSWindow *)window; 

- (BOOL)windowShouldClose:(id)sender;

集合操做类方法( Collection Methods

有些对象管理着一系列其它对象或者元素的集合,须要使用相似增删查改的方法来对集合进行操做,这些方法的命名范式通常为:

// 集合操做范式

- (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;

若是管理的集合元素中有指向管理对象的指针,要设置成 weak 类型以防止引用循环。

下面是 SDK NSWindow 类的集合操做方法:

- (void)addChildWindow:(NSWindow *)childWin ordered:(NSWindowOrderingMode)place;

- (void)removeChildWindow:(NSWindow *)childWin;

- (NSArray *)childWindows;

- (NSWindow *)parentWindow;

- (void)setParentWindow:(NSWindow *)window;

命名函数( Functions

在不少场合仍然须要用到函数,好比说若是一个对象是一个单例,那么应该使用函数来代替类方法执行相关操做。

函数的命名和方法有一些不一样,主要是:

  • 函数名称通常带有缩写前缀,表示方法所在的框架。

  • 前缀后的单词以驼峰表示法显示,第一个单词首字母大写。

函数名的第一个单词一般是一个动词,表示方法执行的操做:

NSHighlightRect

NSDeallocateObject

若是函数返回其参数的某个属性,省略动词:

unsigned int NSEventMaskFromType(NSEventType type)

float NSHeight(NSRect aRect)

若是函数经过指针参数来返回值,须要在函数名中使用 Get

unsigned int NSEventMaskFromType(NSEventType type)

float NSHeight(NSRect aRect)

函数的返回类型是 BOOL 时的命名:

BOOL NSDecimalIsNotANumber(const NSDecimal *decimal)

命名属性和实例变量( Properties&Instance Variables

属性和对象的存取方法相关联,属性的第一个字母小写,后续单词首字母大写,没必要添加前缀。属性按功能命名成名词或者动词:

// 名词属性

@property (strong) NSString *title;

// 动词属性

@property (assign) BOOL showsAlpha;

属性也能够命名成形容词,这时候一般会指定一个带有 is 前缀的 get 方法来提升可读性:

@property (assign, getter=isEditable) BOOL editable;

命名实例变量,在变量名前加上 _ 前缀(有些有历史的代码会将 _ 放在后面 ),其它和命名属性同样:

@implementation MyClass {

BOOL _showsTitle;

}

通常来讲,类须要对使用者隐藏数据存储的细节,因此不要将实例方法定义成公共可访问的接口,可使用 @private @protected 前缀。

按苹果的说法,不建议在除了 init dealloc 方法之外的地方直接访问实例变量,但不少人认为直接访问会让代码更加清晰可读,只在须要计算或者执行操做的时候才使用存取方法访问,我就是这种习惯,因此这里不做要求。

命名常量( Constants

若是要定义一组相关的常量,尽可能使用枚举类型( enumerations ),枚举类型的命名规则和函数的命名规则相同:

// 定义一个枚举,注意带有 `_` 的名称是不会被使用的

typedef enum _NSMatrixMode {

NSRadioModeMatrix = 0,

NSHighlightModeMatrix = 1,

NSListModeMatrix = 2,

NSTrackModeMatrix = 3

} NSMatrixMode;

使用匿名枚举定义 bit map

enum {

NSBorderlessWindowMask = 0,

NSTitledWindowMask = 1 << 0,

NSClosableWindowMask = 1 << 1,

NSMiniaturizableWindowMask = 1 << 2,

NSResizableWindowMask = 1 << 3

};

使用 const 定义浮点型或者单个的整数型常量,若是要定义一组相关的整数常量,应该优先使用枚举。常量的命名规范和函数相同:

const float NSLightGray;

不要使用 #define 宏来定义常量,若是是整型常量,尽可能使用枚举,浮点型常量,使用 const 定义。 #define 一般用来给编译器决定是否编译某块代码,好比经常使用的:

#ifdef DEBUG

注意到通常由编译器定义的宏会在先后都有一个 __ ,好比 __MACH__

命名通知( Notifications

通知经常使用于在模块间传递消息,因此通知要尽量地表示出发生的事件,通知的命名范式是:

触发通知的类名 ] + [Did | Will] + [ 动做 ] + Notification

栗子:

NSApplicationDidBecomeActiveNotification

NSWindowDidMiniaturizeNotification

NSTextViewDidChangeSelectionNotification

NSColorPanelColorDidChangeNotification

注释

读没有注释代码的痛苦你我都体会过,好的注释不只能让人轻松读懂你的程序,还能提高代码的逼格。注意注释是为了让别人看懂,而不是仅仅你本身。

文件注释

每个文件都必须写文件注释,文件注释一般包含

  • 文件所在模块

  • 做者信息

  • 历史版本信息

  • 版权信息

  • 文件包含的内容,做用

一段良好文件注释的栗子:

/*******************************************************************************

Copyright (C), 2011-2013, Andrew Min Chang

File name: AMCCommonLib.h

Author: Andrew Chang (Zhang Min)

E-mail: LaplaceZhang@126.com

Description:

This file provide some covenient tool in calling library tools. One can easily include

library headers he wants by declaring the corresponding macros.

I hope this file is not only a header, but also a useful Linux library note.

History:

2012-??-??: On about come date around middle of Year 2012, file created as "commonLib.h"

2012-08-20: Add shared memory library; add message queue.

2012-08-21: Add socket library (local)

2012-08-22: Add math library

2012-08-23: Add socket library (internet)

2012-08-24: Add daemon function

2012-10-10: Change file name as "AMCCommonLib.h"

2012-12-04: Add UDP support in AMC socket library

2013-01-07: Add basic data type such as "sint8_t"

2013-01-18: Add CFG_LIB_STR_NUM.

2013-01-22: Add CFG_LIB_TIMER.

2013-01-22: Remove CFG_LIB_DATA_TYPE because there is already AMCDataTypes.h

Copyright information:

This file was intended to be under GPL protocol. However, I may use this library

in my work as I am an employee. And my company may require me to keep it secret.

Therefore, this file is neither open source nor under GPL control.

********************************************************************************/

文件注释的格式一般不做要求,能清晰易读就能够了,但在整个工程中风格要统一。

代码注释

好的代码应该是自解释 self-documenting )的,但仍然须要详细的注释来讲明参数的意义、返回值、功能以及可能的反作用。

方法、函数、类、协议、类别的定义都须要注释,推荐采用 Apple 的标准注释风格,好处是能够在引用的地方 alt+ 点击自动弹出注释,很是方便。

有不少能够自动生成注释格式的插件,推荐使用 VVDocumenter


一些良好的注释:

/**

* Create a new preconnector to replace the old one with given mac address.

* NOTICE: We DO NOT stop the old preconnector, so handle it by yourself.

*

* @param type Connect type the preconnector use.

* @param macAddress Preconnector's mac address.

*/

- (void)refreshConnectorWithConnectType:(IPCConnectType)type Mac:(NSString *)macAddress;

/**

* Stop current preconnecting when application is going to background.

*/

-(void)stopRunning;

/**

* Get the COPY of cloud device with a given mac address.

*

* @param macAddress Mac address of the device.

*

* @return Instance of IPCCloudDevice.

*/

-(IPCCloudDevice *)getCloudDeviceWithMac:(NSString *)macAddress;

// A delegate for NSApplication to handle notifications about app

// launch and shutdown. Owned by the main app controller.

@interface MyAppDelegate : NSObject {

...

}

@end

协议、委托的注释要明确说明其被触发的条件:

/** Delegate - Sent when failed to init connection, like p2p failed. */

-(void)initConnectionDidFailed:(IPCConnectHandler *)handler;

若是在注释中要引用参数名或者方法函数名,使用 || 将参数或者方法括起来以免歧义:

// Sometimes we need |count| to be less than zero.

// Remember to call |StringWithoutSpaces("foo bar baz")|

定义在头文件里的接口方法、属性必需要有注释!

编码风格

每一个人都有本身的编码风格,这里总结了一些比较好的 Cocoa 编程风格和注意点。

不要使用 new 方法

尽管不少时候能用 new 代替 alloc init 方法,但这可能会致使调试内存时出现不可预料的问题。 Cocoa 的规范就是使用 alloc init 方法,使用 new 会让一些读者困惑。

Public API 要尽可能简洁

共有接口要设计的简洁,知足核心的功能需求就能够了。不要设计不多会被用到,可是参数极其复杂的 API 。若是要定义复杂的方法,使用类别或者类扩展。

#import #include

#import Cocoa 中经常使用的引用头文件的方式,它能自动防止重复引用文件,何时使用 #import ,何时使用 #include 呢?

  • 当引用的是一个 Objective-C 或者 Objective-C++ 的头文件时,使用 #import

  • 当引用的是一个 C 或者 C++ 的头文件时,使用 #include ,这时必需要保证被引用的文件提供了保护域( #define guard )。

栗子:

#import

#include

#import "GTMFoo.h"

#include "base/basictypes.h"

为何不所有使用 #import 呢?主要是为了保证代码在不一样平台间共享时不出现问题。

引用框架的根头文件

上面提到过,每个框架都会有一个和框架同名的头文件,它包含了框架内接口的全部引用,在使用框架的时候,应该直接引用这个根头文件,而不是其它子模块的头文件,即便是你只用到了其中的一小部分,编译器会自动完成优化的。

// 正确,引用根头文件

#import

// 错误,不要单独引用框架内的其它头文件

#import

#import

BOOL 的使用

BOOL Objective-C 中被定义为 signed char 类型,这意味着一个 BOOL 类型的变量不只仅能够表示 YES(1) NO(0) 两个值,因此永远不要 BOOL 类型变量直接和 YES 比较:

// 错误,没法肯定 |great| 的值是不是 YES(1) ,不要将 BOOL 值直接与 YES 比较

BOOL great = [foo isGreat];

if (great == YES)

// ...be great!

// 正确

BOOL great = [foo isGreat];

if (great)

// ...be great!

一样的,也不要将其它类型的值做为 BOOL 来返回,这种状况下, BOOL 变量只会取值的最后一个字节来赋值,这样极可能会取到 0 NO )。可是,一些逻辑操做符好比 &&,||,! 的返回是能够直接赋给 BOOL 的:

// 错误,不要将其它类型转化为 BOOL 返回

- (BOOL)isBold {

return [self fontTraits] & NSFontBoldTrait;

}

- (BOOL)isValid {

return [self stringValue];

}

// 正确

- (BOOL)isBold {

return ([self fontTraits] & NSFontBoldTrait) ? YES : NO;

}

// 正确,逻辑操做符能够直接转化为 BOOL

- (BOOL)isValid {

return [self stringValue] != nil;

}

- (BOOL)isEnabled {

return [self isValid] && [self isBold];

}

另外 BOOL 类型能够和 _Bool,bool 相互转化,可是不能 Boolean 转化。

使用 ARC

除非想要兼容一些古董级的机器和操做系统,咱们没有理由放弃使用 ARC 。在最新版的 Xcode(6.2) 中, ARC 是自动打开的,因此直接使用就行了。

init dealloc 中不要用存取方法访问实例变量

initdealloc 方法被执行时,类的运行时环境不是处于正常状态的,使用存取方法访问变量可能会致使不可预料的结果,所以应当在这两个方法内直接访问实例变量。

// 正确,直接访问实例变量

- (instancetype)init {

self = [super init];

if (self) {

_bar = [[NSMutableString alloc] init];

}

return self;

}

- (void)dealloc {

[_bar release];

[super dealloc];

}

// 错误,不要经过存取方法访问

- (instancetype)init {

self = [super init];

if (self) {

self.bar = [NSMutableString string];

}

return self;

}

- (void)dealloc {

self.bar = nil;

[super dealloc];

}

按照定义的顺序释放资源

在类或者 Controller 的生命周期结束时,每每须要作一些扫尾工做,好比释放资源,中止线程等,这些扫尾工做的释放顺序应当与它们的初始化或者定义的顺序保持一致。这样作是为了方便调试时寻找错误,也能防止遗漏。

保证 NSString 在赋值时被复制

NSString 很是经常使用,在它被传递或者赋值时应当保证是以复制( copy )的方式进行的,这样能够防止在不知情的状况下 String 的值被其它对象修改。

- (void)setFoo:(NSString *)aFoo {

_foo = [aFoo copy];

}

使用 NSNumber 的语法糖

使用带有 @ 符号的语法糖来生成 NSNumber 对象能使代码更简洁:

NSNumber *fortyTwo = @42;

NSNumber *piOverTwo = @(M_PI / 2);

enum {

kMyEnum = 2;

};

NSNumber *myEnum = @(kMyEnum);

nil 检查

由于在 Objective-C 中向 nil 对象发送命令是不会抛出异常或者致使崩溃的,只是彻底的什么都不干,因此,只在程序中使用 nil 来作逻辑上的检查。

另外,不要使用诸如 nil == Object 或者 Object == nil 的形式来判断。

// 正确,直接判断

if (!objc) {

...

}

// 错误,不要使用 nil == Object 的形式

if (nil == objc) {

...

}

属性的线程安全

定义一个属性时,编译器会自动生成线程安全的存取方法( Atomic ),但这样会大大下降性能,特别是对于那些须要频繁存取的属性来讲,是极大的浪费。因此若是定义的属性不须要线程保护,记得手动添加属性关键字 nonatomic 来取消编译器的优化。

点分语法的使用

不要用点分语法来调用方法,只用来访问属性。这样是为了防止代码可读性问题。

// 正确,使用点分语法访问属性

NSString *oldName = myObject.name;

myObject.name = @"Alice";

// 错误,不要用点分语法调用方法

NSArray *array = [NSArray arrayWithObject:@"hello"];

NSUInteger numberOfItems = array.count;

array.release;

Delegate 要使用弱引用

一个类的 Delegate 对象一般还引用着类自己,这样很容易形成引用循环的问题,因此类的 Delegate 属性要设置为弱引用。

/** delegate */

@property (nonatomic, weak) iddelegate;

相关文章
相关标签/搜索