注释
读没有注释代码的痛苦你我都体会过,好的注释不仅能让人轻松读懂你的程序,还能提升代码的逼格。注意注释是为了让别人看懂,而不是仅仅你自己。
文件注释
每一个文件都必须写文件注释,文件注释通常包含
- 文件所在模块
- 作者信息
- 历史版本信息
- 版权信息
- 文件包含的内容,作用
一段良好文件注释的栗子:
/*******************************************************************************
Copyright (C), 2011-2013, Andrew Min Chang
File name: AMCCommonLib.h
Author: Andrew Chang (Zhang Min)
E-mail: [email protected]
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
转化。
在init和dealloc中不要用存取方法访问实例变量
当init``dealloc
方法被执行时,类的运行时环境不是处于正常状态的,使用存取方法访问变量可能会导致不可预料的结果,因此应当在这两个方法内直接访问实例变量。
//正确,直接访问实例变量
- (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) {
...
}
点分语法的使用
不要用点分语法来调用方法,只用来访问属性。这样是为了防止代码可读性问题。
//正确,使用点分语法访问属性
NSString *oldName = myObject.name;
myObject.name = @"Alice";
//错误,不要用点分语法调用方法
NSArray *array = [NSArray arrayWithObject:@"hello"];
NSUInteger numberOfItems = array.count;
array.release;