代码规范

144 阅读11分钟

1.在 @interface、@implementation 中的代码规范

​  不要求在 @interface@implementation@end 前后空行。如果在 @interface 声明了实例变量,则须在关括号 } 之后空一行。

//  Foo.h
//  AwesomeProject
//
//  Created by Greg Miller on 6/13/08.
//  Copyright 2008 Google, Inc. All rights reserved.
//

#import <Foundation/Foundation.h>

// A sample class demonstrating good Objective-C style. All interfaces,
// categories, and protocols (read: all top-level declarations in a header)
// MUST be commented. Comments must also be adjacent to the object they're
// documenting.
//
// (no blank line between this comment and the interface)
@interface Foo : NSObject {
 @private
  NSString *bar_;
  NSString *bam_;
}

// Returns an autoreleased instance of Foo. See -initWithBar: for details
// about |bar|.
+ (id)fooWithBar:(NSString *)bar;

// Designated initializer. |bar| is a thing that represents a thing that
// does a thing.
- (id)initWithBar:(NSString *)bar;

// Gets and sets |bar_|.
- (NSString *)bar;
- (void)setBar:(NSString *)bar;

// Does some work with |blah| and returns YES if the work was completed
// successfully, and NO otherwise.
- (BOOL)doWorkWithBlah:(NSString *)blah;

@end
//
//  Foo.m
//  AwesomeProject
//
//  Created by Greg Miller on 6/13/08.
//  Copyright 2008 Google, Inc. All rights reserved.
//

#import "Foo.h"


@implementation Foo

+ (id)fooWithBar:(NSString *)bar {
  return [[[self alloc] initWithBar:bar] autorelease];
}

// Must always override super's designated initializer.
- (id)init {
  return [self initWithBar:nil];
}

- (id)initWithBar:(NSString *)bar {
  if ((self = [super init])) {
    bar_ = [bar copy];
    bam_ = [[NSString alloc] initWithFormat:@"hi %d", 3];
  }
  return self;
}

- (void)dealloc {
  [bar_ release];
  [bam_ release];
  [super dealloc];
}

- (NSString *)bar {
  return bar_;
}

- (void)setBar:(NSString *)bar {
  [bar_ autorelease];
  bar_ = [bar copy];
}

- (BOOL)doWorkWithBlah:(NSString *)blah {
  // ...
  return NO;
}

@end

2.留白与空格

1)空格与制表符

​  使用两个空格进行缩进,不使用制表符。

2)行宽

​  行宽保持在 80 列之内。

3)方法声明、方法定义

​  在前缀符 +、- 与返回类型之间要有一个空格,参数之间要有空格。

​  指针类型的 * 与参数类型之间的空格是可选的,与之前的保持一致即可

​  若参数较多,最好将每个参数单独放在一行,并将参数之前的冒号对齐。

​  - (void)doSomethingWith:(GTMFoo *)theFoorect:(NSRect)theRectinterval:(float)theInterval { //do something}

​  若关键字较长,使得冒号无法对齐,则保证下一行至少有四个空格的缩进,并使得后续各行关键字垂直对齐。

- (void)short:(GTMFoo *)theFoo
    longKeyword:(NSRect)theRect 
    evenLongerKeyword:(float)theInterval {
    //do something 
 }

4)方法调用

​  调用时所有参数应在同一行;或者每行一个参数,以冒号对齐。

5)访问修饰符

​  @public、@private 访问修饰符应缩进一个空格。

6)异常

​  异常关键字应独占一行,异常关键字与 {} 之间需要有一个空格。 @catch 与之后的异常对象之间也要有一个空格。

7)协议名

​  类型标识符与协议名之间,不能有任何空格。

@interface MyProtocoledClass : NSObject<NSWindowDelegate> {

}

@end

8)代码块

​  如果一行可以写完代码块,则没必要换行。

​  如果不得不换行,关括号应与块声明的第一个字符对齐。

​  块内的代码须按 4 空格缩进。

​  如果块太长,比如超过 20 行,建议将其定义成一个局部变量,然后再使用该变量。

3.命名

    不要使用缩略词。

1)文件名

​  文件名须反映出其实现了什么类,并且要区分大小写。

​  类别的文件名应该包含被扩展的类名,如:GTMNSString+Utils.hGTMNSTextView+Autocomplete.h

2)Objective-C 与 C++ 混合时

​  根据使用的语言选择编码风格,实现 @implementation 语句块时,使用 Objective-C 的命名规则。实现一个 C++ 的类,就使用 C++ 命名规则。

3)类名、类别名、协议名

​  首字母大写,并以驼峰格式分割单词。应用层的代码,类名应该尽量避免不必要的前缀。

     Protocol一般定义在本类的.h文件中,请定义在@interface上方,如果协议方法过多,建议新建协议文件定义协议。

4)类别名

​  类别名应该有两三个字母的前缀以表示类别是项目的一部分或者该类别是通用的。类别名应该包含它所扩展的类的名字。

​  类名与类别名之间,要以一个空格分隔。

5)方法名

​  以小写字母开头,并混合驼峰格式。参数名称也应该以小写字母开头。

​  方法名与参数结合起来应尽量读起来就像句子,这表示应该选择与方法名连在一起读起来通顺的参数名。(例如,convertPoint:fromRect:replaceCharactersInRange:withString:)。

​  getter 方法不应以 get 作为前缀。

6)变量名

​  变量名应该以小写字母开头,并使用驼峰格式。

​  类的成员变量应该以下划线作为后缀,并混合大小写。

​  尽量为变量起一个描述性的名字,不用担心名称太长,便于理解更加重要。

​  常量名(如宏定义、枚举、静态常量等)应该以小写字母 k 开头,使用驼峰格式分隔单词,如:kInvalidHandle,kWritePerm

  全局变量应以小写字母 'g'开头,使用驼峰格式分隔单词。

4.注释

1)文件注释

​  以内容的简要描述为起始,接着是作者名称,最后是版权声明、许可证样板。

2)声明部分的注释

​  每个接口、类别以及协议应辅以注释,以描述它的目的及与整个项目的关系。

​  如果已在文件头部详细描述了接口,可直接说明 “完整的描述请参见文件头部”,一定要有这部分注释。

​  公共接口的每个方法,都应该有注释来解释它的作用、参数、返回值以及其它影响。

​  如果类的实例可以被多个线程访问,记得注释多线程条件下的使用规则。

3)实现部分的注释

​  使用 | 来引用注释中的变量名及符号名而不是使用引号,因为变量名或符号名中可能含有引号。

4)对象所有权

​  继承自 NSObject 的对象的实例变量指针,通常被假定是强引用关系(retained)

​  声明的属性如果没有被类 retained,必须指定是弱引用或赋予 @property 属性。

​  标记上 IBOutlets 的实例变量,被认为是不会被类 retained 的。

​  当实例变量指向 CoreFoundation、C++ 或者其它非 Objective-C 对象时,不论指针是否会被 retained,都需要使用 __strong__weak 类型修饰符明确指明。

​  CoreFoundation 和其它非 Objective-C 对象指针需要显式的内存管理,即便使用了自动引用计数或垃圾回收机制。

​  当不允许使用 __weak 类型修饰符(比如,使用 clang 编译时的 C++ 成员变量)时,应使用注释替代说明。

​  强引用 - 对象被类 retained。弱引用 - 对象没有被类 retained,如委托。

5.Cocoa 与 Objective-C 特性

1)成员变量

​  应被声明为 @private。

​  未声明实例变量的接口,应省略 {}。

​  优先考虑使用 @synthesize 合成成员变量,即 @synthesize var = var_;,而非直接声明成员变量。

2)构造函数

​  应明确 指定构造函数。

​  如果未禁止调用系统默认的初始化函数,则需要在默认的初始化函数中写入构造逻辑。因为有时系统会调用默认的初始化函数,而非自定义的初始化函数。

​  不要在 init 方法中,初始化成员变量,这一做法毫无必要。因为刚分配的对象的成员变量都有默认的初始值,除了 isa 指针(NSObjectisa 指针,用于标识对象的类型)。

​  不要调用 NSObject 类方法 new,也不要在子类中重载它。使用 allocinit 方法创建并初始化对象。

3)重写 NSObject 的方法

​  如果重写了 NSObject 类的方法,建议将其放在 @implementation 的起始处。

4)如果不希望外界调用某个函数,那么就不要公开此函数

​  除非客户端的代码期望使用某个方法,否则不要把这个方法放进公共接口中。这样可以尽可能的避免不希望被调用的方法却被调用到。对于内部实现所需要的方法,在实现的文件中定义一个类别,而不是把它们放进公有的头文件中。

​  私有的方法应该有一个相当特殊的名字以防止子类无意地重写它们。

​  类别可以用来将一个大的 @implementation 拆分成更容易理解的小块。同时,类别可以为最适合的类添加新的、特定应用程序的功能,这要比创建一个新类,而后把方法放进里面好得多。

5)#import 与 #include

​  #import Objective-C/Objective-C++ 头文件,#include C/C++ 头文件。

​  对于跨平台项目,在所有的平台上统一使用 #include,意味着构造更可能全都成功或者失败,从而避免了这些文件只能在某些平台下工作。

6)#import 根框架

​  当试图从框架(如 Cocoa 或者 Foundation)中包含若干零散的系统头文件时,实际上选择包含顶层根框架的话,编译器要做的工作更少。因为根框架通常已经预编译完成,加载速度更快。对于 Objective-C 的框架,使用 #import 来包含。

7)autorelease

​  当创建临时对象时,随机便设定 autorelease,而不是在后面再进行 release

​  尽管运行效率会差一点,但避免了因意外删除 release 或者插入 return 语句而导致的内存泄露。

​  给对象赋值时遵守 autorelease之后 ``retain` 的模式,即:

(void)setFoo:(GMFoo *)aFoo {
    [foo_ autorelease]; // Won't dealloc if |foo_| == |aFoo|
    foo_ = [aFoo retain];
}

8)setter、getter方法

​  在 init 方法中,子类未完全初始化。在 dealloc 方法中,子类可能已被释放。在这两种方法中,要避免使用 setter、getter方法(包括点表示法),应直接对变量进行赋值或释放操作。因为在setter、getter 方法中,可能使用变量进行了额外的操作,比如将变量的值赋予其他变量。

​  接受 NSString 作为参数的 setter,应该总是 copy 传入的字符串。永远不要仅仅 retain 一个字符串。因为调用者很可能在你不知情的情况下修改了字符串。

- (void)setFoo:(NSString *)aFoo {[foo_ autorelease];foo_ = [aFoo copy];}

9)按照声明顺序销毁成员变量

​  dealloc 中实例变量被释放的顺序应该与它们在 @interface 中声明的顺序一致,这有助于代码审查。

​  如果 dealloc 调用了其它方法释放成员变量,应添加注释解释这些方法释放了哪些实例变量。

10)避免抛出异常

​  不使用 @throw。为了合理使用第三方的代码,@try@catch@finally 是允许的。如果确实使用了异常,请明确注释期望什么方法抛出异常。

​  如果抛出 Objective-C 异常,Objective-C++ 代码中基于栈的对象不会被销毁。如果必须在 Objective-C++ 中使用异常,就只用 C++ 的异常机制。永远不应该重新抛出 Objective-C 异常,也不应该在 @try@catch@finally 语句块中使用基于栈的 C++ 对象。

11)nil

​  使用 nil 的检查来检查应用程序的逻辑流程,而不是避免崩溃。Objective-C 运行时会处理向 nil 对象发送消息的情况,这不同于 C/C++,因为 C/C++ 运行时不做检查,所以空指针可能会导致程序崩溃。

12)BOOL

​  不要将普通整型值直接转换为 BOOL 值。当转换整型值至 BOOL 时,使用三目操作符来返回 YES 或者 NO

​  对 BOOL 使用逻辑运算符(&&||!)是合法的,返回值也可以安全地转换成 BOOL

​  不要直接将 BOOL 值与 YES/NO 进行比较,因为32bit系统中 BOOL 值是有符号字符型。

   BOOL great = [foo isGreat];
   if (great == YES) // avoid
   if(great)        //good

13)属性

​  点表示法只允许用于属性,不能用于属性对应的成员变量。

​  属性所关联的实例变量的名称:someName_,属性名称:someName。

​  属性的声明必须紧靠着类接口中的成员变量语句块。

​  属性的定义必须在 @implementation 模块的最上方。

​  属性的缩进与包含他们的 @interface 以及 @implementation 语句一样。

​  应总是用 copy 属性声明 NSString 属性。

​  要注意属性的开销。缺省情况下,所有 settergetter 都是原子的,这会给每个 getter 或者 setter 带来一定的同步开销。除非需要原子性,否则最好将属性声明为 nonatomic

6.Cocoa 模式

1)委托模式

​  实现委托模式的类应拥有一个名为 delegate_ 的实例变量来引用委托。

​  因此,访问器方法应该命名为 delegatesetDelegate:

​  delegate_ 对象不应该被 retain

2)MVC

​  分离模型与视图:不要假设模型或者数据源的表示方法。保持数据源与表示层之间的接口抽象。视图不需要了解模型的逻辑。

​  分离控制器与模型、视图:不要把所有的 “业务逻辑” 放进跟视图有关的类中,这会使代码非常难以复用。使用控制器类来处理这些代码,但保证控制器不需要了解太多表示层的逻辑。

​  使用 @protocol 来定义回调 API,如果不是所有的方法都必须实现,使用 @optional

参考文章

zh-google-styleguide.readthedocs.io/en/latest/g…

google.github.io/styleguide/…