OC代码规范

基本:代码要分层,遵循MVC模式,代码不能混在一起,方法不易过长,方法变量名不允许中英文混合。

一、空格

1,缩进使用4个空格,确保在Xcode偏好设置来设置
2,方法大括号和其他大括号(if/else/switch/while 等.)要单独一行。
应该:
if(user.isHappy)
{
//Do something
}
else
{
//Do something else
}
3,赋值操作符左右留1个空格,= += -=。
int a = b;

二、Protocol

 在书写协议的时候注意用<>括起来的协议和类型名之间是没有空格的,适用所有书写协议的地方,包括函数声明、类声明、实例变量等. eg

@interface MyProtocoledClass : NSObject
{
id delegate;
}
- (void)setDelegate:(id)aDelegate;
@end

三、Block

较短的block可以写在一行内。
如果分行显示的话,block的右括号}应该和调用block那行代码的第一个非空字符对齐。
block内的代码采用4个空格的缩进。
如果block过于庞大,应该单独声明成一个变量来使用。
^和(之间,^和{之间都没有空格,参数列表的右括号)和{之间有一个空格。
eg:
//较短的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应该单独定义成变量使用
void (^largeBlock)(void) = ^{
// …
};
//在一个调用中使用多个block,注意到他们不是像函数那样通过’:’对齐的,而是同时进行了4个空格的缩进
[myObject doSomethingWith:arg1
firstBlock:^(Foo *a) {
// …
}
secondBlock:^(Bar *b) {
// …
}];

四、命名

1,基本规则
清晰:命名应该尽可能的清晰和简洁,不必担心名称过长的问题。
一致性:整个工程的命名风格要保持一致性,最好和苹果SDK的代码保持统一。不同类中完成相似功能的方法应该叫一样的名字,比如我们总是用count来返回集合的个数,不能在A类中使用count而在B类中使用getNumber。
2,使用前缀
1. 前缀由大写的字母缩写组成,比如Cocoa中NS前缀代表Founation框架中的类,IB则代表Interface Builder框架。
2. 可以在为类、协议、函数、常量以及typedef宏命名的时候使用前缀,但注意不要为成员变量或者方法使用前缀,因为他们本身就包含在类的命名空间内。
3. 命名前缀的时候不要和苹果SDK框架冲突。
3,命名类和协议(Class&Protocol)
类名以大写字母开头,应该包含一个名词来表示它代表的对象类型,同时可以加上必要的前缀,比如NSString, NSDate, NSScanner, NSApplication等等。
而协议名称应该清晰地表示它所执行的行为,而且要和类名区别开来,所以通常使用ing词尾来命名一个协议,比如NSCopying,NSLocking。
有些协议本身包含了很多不相关的功能,主要用来为某一特定类服务,这时候可以直接用类名来命名这个协议,比如NSObject协议,它包含了id对象在生存周期内的一系列方法。
4,方法(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;
方法的参数命名也有一些需要注意的地方:

五、函数(Functions)

在很多场合仍然需要用到函数,比如说如果一个对象是一个单例,那么应该使用函数来代替类方法执行相关操作。
函数的命名和方法有一些不同,主要是:
* 函数名称一般带有缩写前缀,表示方法所在的框架。
* 前缀后的单词以“驼峰”表示法显示,第一个单词首字母大写。
函数名的第一个单词通常是一个动词,表示方法执行的操作:
NSHighlightRect
NSDeallocateObject
如果函数返回其参数的某个属性,省略动词:
unsigned int NSEventMaskFromType(NSEventType type)
float NSHeight(NSRect aRect)
如果函数通过指针参数来返回值,需要在函数名中使用Get:
const char *NSGetSizeAndAlignment(const char *typePtr, unsigned int *sizep, unsigned int *alignp)
函数的返回类型是BOOL时的命名:
BOOL NSDecimalIsNotANumber(const NSDecimal *decimal)

六、属性和实例变量,常量

常量应该使用驼峰式命名规则,所有的单词首字母大写和加上与类名有关的前缀。
属性也是使用驼峰式,但首单词的首字母小写,对属性使用auto-synthesis。
当使用属性时,实例变量应该使用self.来访问和改变。这就意味着所有属性将会视觉效果不同,因为它们前面都有self.。
但有一个特例:在初始化方法里,实例变量(例如,_variableName)应该直接被使用来避免getters/setters潜在的副作用。
局部变量不应该包含下划线。

七、类的属性和成员变量

@interface ClassA : NSObject
{
//1,申明成员变量,子类可以访问,外部无法访问
NSString *content;
}
@property (nonatomic,copy) NSString * title;
@property (nonatomic,copy,readonly) NSString *name;
@end
//class extension
@interface ClassA ()
{
//2,申明成员变量,只能在本文件中访问
NSString *privateVariable;
}
//可以修改.h文件中的property的权限,方便.m中修改。
@property (nonatomic,copy,readwrite) NSString *name;
@end
@implementation ClassA
{
//3,申明成员变量,只能在本文件中访问
NSString *anotherInstanceVariable;
}
//@synthesize 可不要
//@synthesize tile;
@end;
声明成员变量有以上三种方式,子类需要访问的使用的用1,不需要就用2。为与之前代码一致,不用三的方式。
property变量title除了在init和dealloc中用_title方式访问,在.m中以self.title的方式访问,不用title方式。

八、其他事项

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

2,Public API要尽量简洁
共有接口要设计的简洁,满足核心的功能需求就可以了。不要设计很少会被用到,但是参数极其复杂的API。如果要定义复杂的方法,使用类别或者类扩展。

3,#import和#include
import是Cocoa中常用的引用头文件的方式,它能自动防止重复引用文件,什么时候使用#import,什么时候使用#include呢?
* 当引用的是一个Objective-C或者Objective-C++的头文件时,使用#import
* 当引用的是一个C或者C++的头文件时,使用#include,这时必须要保证被引用的文件提供了保护域(#define guard)。

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

4,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转化。

5,在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];
}

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

7,保证NSString在赋值时被复制
NSString非常常用,在它被传递或者赋值时应当保证是以复制(copy)的方式进行的,这样可以防止在不知情的情况下String的值被其它对象修改。
- (void)setFoo:(NSString *)aFoo
{
_foo = [aFoo copy];
}

8,使用NSNumber的语法糖
使用带有@符号的语法糖来生成NSNumber对象能使代码更简洁:
NSNumber *fortyTwo = @42;
NSNumber *piOverTwo = @(M_PI / 2);
enum
{
kMyEnum = 2;
};
NSNumber *myEnum = @(kMyEnum);

9,nil检查
因为在Objective-C中向nil对象发送命令是不会抛出异常或者导致崩溃的,只是完全的“什么都不干”,所以,只在程序中使用nil来做逻辑上的检查。
另外,不要使用诸如nil == Object或者Object == nil的形式来判断。
//正确,直接判断
if (!objc)
{

}

//错误,不要使用nil == Object的形式
if (nil == objc)
{

}

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

11,点分语法的使用
不要用点分语法来调用方法,只用来访问属性。这样是为了防止代码可读性问题。
//正确,使用点分语法访问属性
NSString *oldName = myObject.name;
myObject.name = @”Alice”;

//错误,不要用点分语法调用方法
NSArray *array = [NSArray arrayWithObject:@”hello”];
NSUInteger numberOfItems = array.count;
array.release;

12,Delegate要使用弱引用
一个类的Delegate对象通常还引用着类本身,这样很容易造成引用循环的问题,所以类的Delegate属性要设置为弱引用。
/* delegate /
@property (nonatomic, weak) id delegate;

猜你喜欢

转载自blog.csdn.net/lklvzoely/article/details/82593397
今日推荐