NYTimes Objective-C 编程风格指南

转自eseedo的博客 [微博]

NYTimes Objective-C 编程风格指南。来源:https://github.com/NYTimes/objective-c-style-guide

这篇指南总结了纽约时报iOS开发团队的编程风格。欢迎大家在github中提供建议和pull请求。

简介:

以下是形成本编程指南所涉及到的Apple官方文档。如果本文有未尽之处,可以参考以下链接:

The Objective-C Programming Language

Cocoa Fundamentals Guide

Coding Guidelines for Cocoa

iOS App Programming Guide

目录

点表示法

空格

条件语句

三元运算子

方法

变量

命名

下划线

注释

初始化&内存释放

Literals字面量

CGRect 函数

常量

枚举类型

私有属性

图片名称

布尔变量

单例

Xcode项目

点表示法应“仅”用于获取和改变属性。括号表示法用于所有其它实例。

例如:

恰当用法:

view.backgroundColor = [UIColor orangeColor];
[UIApplication sharedApplication].delegate;

不当用法:

[view setBackgroundColor:[UIColor orangeColor]];
UIApplication.sharedApplication.delegate;

空格

(1)行缩进使用4个空格。禁止使用Tab键来缩进。请在Xcode偏好设置中进行设置。

(2)方法大括号和其它大括号(比如if/else/switch/while等等)应在语句的同一行开始,而在新的一行关闭。

例如:

if (user.isHappy) {
//Do something
}
else {
//Do something else
}

(3)为保证视觉上的整洁和代码组织,在方法之间应提供且仅提供一行空白。方法中的空白应用于区分功能,但空白行最好用于区分两个不同方法。

(4)@synthesize和@dynamic应在方法实现的新一行中声明。

条件语句

为避免错误,条件语句体必须使用大括号,即便语句体中的语句可以不必使用大括号(比如只有一行语句)。常见的错误包括在不使用大括号的情况下添加第二行语句,以为它属于if语句的一部分。此外,更可怕的事情是,如果条件语句中的代码行被注释,则本不术语条件语句的下一行代码将变成条件语句的一部分。此外,这种编码风格和所有其它条件语句均保持一致。

例如:

恰当用法:

if (!error) {
    return success;
}
 

不当用法:

if (!error)
    return success;

不当用法2:

if (!error) return success;

三元运算子

仅当使用该运算子可以让代码显得更清晰易懂时方可使用三元运算子。更多情况下应使用条件语句。使用类似if的条件语句对多种条件进行判断通常要更容易理解,或使用实例变量。

恰当用法:

result = a > b ? x : y;

不当用法:

result = a > b ? x = c > d ? c : d : y;

方法

在方法声明中,在(-/ )符号之后应加上一个空格。此外,在方法段之间应添加一个空格。

例如:

(void)setExampleText:(NSString *)text image:(UIImage *)image;

变量

变量的命名应尽可能具有自解释性。除了在for()循环语句中,应避免使用单个字母变量名称。

除非是常量,星号应紧贴变量名称表示指向变量的指针,比如:

正确用法:

NSString *text;

不当用法:

NSString* text;
 
NSString * text;

应尽可能使用属性定义替代单一的实例变量。避免在初始化方法,dealloc方法和自定义的setter和getter方法中直接读取实例变量参数(init,initWithCoder:,等等)。更多信息请参看here

例如:

恰当用法:

@interface NYTSection: NSObject
 
@property (nonatomic) NSString *headline;
 
@end

不当用法:

@interface NYTSection : NSObject {
    NSString *headline;
}

命名规范

苹果的命名规范应尽可能符合内存管理法则(NARC)memory management rules

在Objective-C中鼓励使用长的描述性的方法和变量名称。

例如:

恰当用法:UIButton *settingsButton;

不当用法:

UIButton *setBut;

对于类和常量名称,应尽量使用三大写字母前缀(比如NYT),但对Core Data的实体名称可不适用该法则。

常量名称应将其中的所有单词的首字母大写,同时加上相关类的名称作为前缀。

例如:

恰当用法:

static const NSTimeInterval NYTArticleViewControllerNavigationFadeAnimationDuration = 0.3;

不当用法:

static const NSTimeInterval fadetime = 1.7;

属性名称应使用camel-case(驼峰式)命名方法,且第一个单词的首字母应为小写。如果Xcode版本支持对变量的自动合成,则不必深究。否则与该属性对应的实例变量名称的第一个单词的首字母应为小写,且在前面加上下划线。

例如:

恰当用法:

@synthesize descriptiveVariableName = _descriptiveVariableName;

不当用法:

id varnm;

下划线

当使用属性变量时,应通过self.来获取和更改实例变量。这就意味着所有的属性将是独特的,因为它们的名称前会加上self.。本地变量名称中不应包含下划线。

注释

在需要注释的地方,应使用注释来解释某一块特定的代码的功能。所有的代码注释必须是最新的,要吗就删掉。

应尽量使用行注释,而避免使用块注释。之所以这样是因为代码自身需要是自文档化的,因此只需要零散添加一些行注释。当然,对于用于生成文档的注释,该原则并不适用。

初始化和内存释放

dealloc方法应放在方法实现文件的顶部,在@synthesize和@dynamic语句之后。init初始化方法应放在dealloc方法之后。

Literals字面量

在创建NSString,NSDictionary,NSArray和NSNumber等对象的immutable实例时,应使用字面量。需要注意的是,不应将nil传递给NSArray和NSDictionary字面量,否则会引起程序崩溃。

例如:

恰当用法:

NSArray *names = @[@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul"];
NSDictionary *productManagers = @{@"iPhone" : @"Kate", @"iPad" : @"Kamal", @"Mobile Web" : @"Bill"};
NSNumber *shouldUseLiterals = @YES;
NSNumber *buildingZIPCode = @10018;

不当用法:

NSArray *names = [NSArray arrayWithObjects:@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul", nil];
NSDictionary *productManagers = [NSDictionary dictionaryWithObjectsAndKeys: @"Kate", @"iPhone", @"Kamal", @"iPad", @"Bill", @"Mobile Web", nil];
NSNumber *shouldUseLiterals = [NSNumber numberWithBool:YES];
NSNumber *ZIPCode = [NSNumber numberWithInteger:10018];

CGRect函数

当需要获取一个CGRect矩形的x,y,width,height属性时,应使用CGGeometry函数,而非直接访问结构体成员。

例如:

恰当用法:

CGRect frame = self.view.frame;
 
CGFloat x = CGRectGetMinX(frame);
CGFloat y = CGRectGetMinY(frame);
CGFloat width = CGRectGetWidth(frame);
CGFloat height = CGRectGetHeight(frame);

不当用法:

CGRect frame = self.view.frame;
 
CGFloat x = frame.origin.x;
CGFloat y = frame.origin.y;
CGFloat width = frame.size.width;
CGFloat height = frame.size.height;

常量

相对字符串字面量或数字,我们更推荐适用常量。应使用static方式声明常量,而非使用#define的方式来定义宏。

例如:

恰当用法:

static NSString * const NYTAboutViewControllerCompanyName = @"The New York Times Company";  
 
static const CGFloat NYTImageThumbnailHeight = 50.0;

不当用法:

#define CompanyName @"The New York Times Company"
 
#define thumbnailHeight 2

枚举类型

在使用enum的时候,推荐适用最新的fixed underlying type(WWDC 2012 session 405- Modern Objective-C)规范,因为它具备更强的类型检查和代码完成功能。

例如:

typedef NS_ENUM(NSInteger, NYTAdRequestState) {
    NYTAdRequestStateInactive,
    NYTAdRequestStateLoading
};
 
私有属性

私有属性应在类实现文件的类扩展(匿名分类)中进行声明。应避免使用命名分类(比如NYTPrivate或private)。

例如:

@interface NYTAdvertisement ()
 
@property (nonatomic, strong) GADBannerView *googleAdView;
@property (nonatomic, strong) ADBannerView *iAdView;
@property (nonatomic, strong) UIWebView *adXWebView;
 
@end

图片名称

在命名图片名称的时候,应保持一致性,从而让开发团队和成员可以明白其含义。图片名称的第一个单词应描述其用途,并使用camel-case风格,然后是不带前缀的所属类名称或属性,最后是色彩、位置和状态。

例如:

RefreshBarButtonItem / RefreshBarButtonItem@2x and RefreshBarButtonItemSelected / RefreshBarButtonItemSelected@2x
ArticleNavigationBarWhite / ArticleNavigationBarWhite@2x and ArticleNavigationBarBlackSelected / ArticleNavigationBarBlackSelected@2x.

布尔变量

因为nil将被解析为NO,因此没有必要在条件语句中进行比较。永远不要将任何东西和YES进行直接比较,因为YES被定义为1,而一个BOOL变量可以有8个字节。

例如:

恰当用法:

if (!someObject) {

}

不当用法:

if (someObject == nil) {
}

以下是BOOL变量的使用:

恰当用法:

if (isAwesome)
if (![someObject boolValue])

不当用法:

if ([someObject boolValue] == NO)
if (isAwesome == YES) // Never do this.

如果一个BOOL属性使用形容词来表达,属性将忽略’is’前缀,但会强调惯用名称。

例如:

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

单例

在创建单例对象的共享实例时,应使用线程安全模式。

例如:

(instancetype)sharedInstance {
   static id sharedInstance = nil;
 
   static dispatch_once_t onceToken;
   dispatch_once(&onceToken, ^{
      sharedInstance = [[self alloc] init];
   });
 
   return sharedInstance;
}

Xcode项目

为避免文件混乱,实际的物理文件应和Xcode项目保持一直。在Xcode中所创建的任何group都应有文件系统中相对应的文件夹。不应仅根据文件类型来进行分组,还需要考虑到其作用。

在Xcode的target的Build Setting中,中尽量开启”Treat Warnings as Errors“,同时尽量开启其他的警告additional warnings。如果需要忽略某个特定的警告,可以使用Clang's pragma feature

如果以上编码风格不合你的口味,还可以参考以下几个风格指南:

Google

GitHub

Adium

Sam Soffes

CocoaDevCentral

Luke Redpath

Marcus Zarra