首页

时间和精神的房子
壹只iOS程序员的修行世界,欢迎来访

如果文章对您有所帮助
将是我最大的荣幸

Cocoa代码风格指南之排版规范(二)

本文基于 Google 的代码风格指南中关于排版规范的部分的总结,其中部门内容具有主观性。本文所有代码的排版均为建议的格式,如有文字上疏漏的地方,可以参考任何一段代码。首先通过一段代码来了解基本的排版格式。

  • :Apple 明确给出建议
  • :Google 明确给出建议
  • :通用做法(主观)
#import <Foundation/Foundation.h>

@interface Foo : NSObject

+ (instancetype)fooWithBar:(NSString *)bar;

- (instancetype)initWithBar:(NSString *)bar;

- (NSString *)bar;
- (void)setBar:(NSString *)bar;

- (BOOL)doWorkWithBlah:(NSString *)blah;

@end
#import "Foo.h"

@implementation Foo {
    NSString *bar;
    NSString *bam;
}

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

- (id)init {
    return [self initWithBar:nil];
}

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

- (NSString *)bar {
    return _bar;
}

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

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

@end

上述代码中的所有换行、空格、缩进都为建议的格式。下边将进行更加详细的描述。

空格和制表符

Google 建议使用2个空格来进行缩进,并且将编辑器设置成自动将制表符替换成空格。但这不符合大多数 Objective-C 程序员的习惯。所以还是建议所有的缩进都使用单个制表符。

行宽

尽量让你的代码保持在 80 列之内。Objective-C 是一门繁冗的语言,在某些情况下略超 80 列可能有助于提高可读性,但这也只能是特例而已,不能成为开脱。如果阅读代码的人认为把把某行行宽保持在 80 列仍然有不失可读性,你应该按他们说的去做。这条规则是有争议的,但很多已经存在的代码坚持了本规则,所以 Google 觉得保证一致性更重要。通过设置 Xcode > Preferences > Text Editing > Show page guide,来使越界更容易被发现。这条规则对我个人有很强的约束,但很多代码难以实现80列,所以在这里还有很多值得探索的写法。

属性

@property (nonatomic, strong) NSString *string;
@property (nonatomic, weak) id<XXDelegate> delegate;
  • property 后留空格
  • nonatomic 放在修饰符第一位
  • 内存管理修饰符放在第二位
  • 修饰符中间在“,”后留空格
  • 修饰符“()”后留空格
  • 所有变量的类名后留空格
  • 类型标识符和尖括号内的协议名之间不能有任何空格

方法

- (void)doSomethingWith:(GTMFoo *)theFoo
                   rect:(NSRect)theRect
               interval:(float)theInterval {
    // TODO
}
- (void)short:(GTMFoo *)theFoo
    longKeyword:(NSRect)theRect
    evenLongerKeyword:(float)theInterval {
    // TODO
}
  • 方法返回值前留空格
  • 方法返回值后不留空格
  • 左括号和方法名在同行并有空格隔开
  • 多个参数的方法换行并以冒号对齐
  • 换行后如有某行过长导致冒号无法对齐则整体缩进一个制表符
  • 方法在声明、实现、调用时均上述规则

Block

// 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) {
        // TODO
    }
});

// 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) = ^{
    // TODO
};
[operationQueue_ addOperationWithBlock:largeBlock];
  1. 如果一行可以写完块,则没必要换行。
  2. 如果不得不换行,关括号应与块声明的第一个字符对齐。
  3. 块内的代码须按 4 空格缩进。
  4. 如果块太长,比如超过 20 行,建议把它定义成一个局部变量,然后再使用该变量。
  5. 如果块不带参数,^{ 之间无须空格。如果带有参数,^( 之间无须空格,但 ) { 之间须有一个空格。
  6. 块内允许按两个空格缩进,但前提是和项目的其它代码保持一致的缩进风格。

  • 如果重载了 NSObject 类的方法,强烈建议把它们放在 @implementation 内的起始处,这也是常见的操作方法。通常适用(但不局限)于 init...,copyWithZone:,以及 dealloc 方法。所有 init... 方法应该放在一起,copyWithZone: 紧随其后,最后才是 dealloc 方法。

  • 指定初始化方法使用 NS_DESIGNATED_INITIALIZER 标示。

  • 写子类时如果需要 init… 方法,记得重载父类的指定构造函数。

// UIView
- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        // TODO
    }
    return self;
}

关注作者

分享本文

目录