您的位置:首页 > 移动开发 > Objective-C

Objective-C编码规范

2016-03-24 11:26 375 查看
这篇我是从这里翻译来的:https://github.com/NYTimes/objective-c-style-guide

在苹果公司提供的以下文档中有涉及到Objective-C编码规范的方方面面,我们可以在以下文档中找到我们需要知道的Objective-C编码规范:

The Objective-C Programming Language

Cocoa Fundamentals Guide

Coding Guidelines for Cocoa

iOS App Programming Guide

点标记语法

建议使用”.”号设置和获取属性:

如:

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


不建议:

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


间隔问题

缩进必须使用4个空格。不要使用tab键进行缩进。确保在xcode的偏好设置中设置这一偏好。

方法的大括号,以及其他的一些大括号(如if/else/switch/while等)都在声明的同一行打开,在新的一行关闭。

如:

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


为了确保代码的美观整洁,以及组织性,在方法之间应该有一行空行。

方法内部的空行可以分隔功能,在这种情况下我们也许可以考虑将这个方法写成若干个更小的方法。

对于方法名冗长的方法,我们可以增加一条空白行来分离方法体与方法名。

@synthesize 和 @dynamic 每一个都应该在@implementation的新的一行中声明。

条件语句

为了避免错误,条件语句中的运行代码需要使用大括号包起来,就算可以不使用大括号的情况(例如只有一行)。否则在想为if语句执行体中添加一行新代码时,容易出错。 而且在执行体只有没有添加大括号的一行时,执行体中的那行代码被注释,导致下一行成为执行体时,情况更严重。

除此之外,这种风格与其它情况的条件语句更为一致,代码一致性,可读性更强。

例如:

if (!error) {
return success;
}


而不要使用:

if (!error)
return success;

//或者

if (!error) return success;


三元运算符

使用三元运算符 ? : 的目的在于让代码更简洁清晰。

三元运算符在每个表达式中应该只用于一个条件。如果用于多个条件会导致代码更加难以理解。

例如:

result = a > b ? x : y;


不要用成:

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


错误处理

当方法通过应用返回一个error参数时, 代码必须在返回的值的基础上进行判断而不能在返回的error变量的基础上进行判断(code MUST switch on the returned value and MUST NOT switch on the error variable.--原文,不太会翻译)

例如:

NSError *error;
if (![self trySomethingWithError:&error]) {
// Handle Error
}


不能:

NSError *error;
[self trySomethingWithError:&error];
if (error) {
// Handle Error
}


因为有时候在成功的时候,一些苹果的API也会写一个垃圾值到error参数中,所以对error变量进行判断的话会导致错误判断(可能会引发后续的crash)。

方法

在方法声明中,需要在方法范围(”-“或”+”号)后面有一个空格。方法的每一节之间也应该有一个空格。

例如:

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


变量

描述性地为变量命名,变量名应该能够清楚地传达它时什么样的变量以及为了正确地使用该变量程序员需要什么相关信息。

例如:

//title是一个string
NSString *title;

//暗示着狮包含html的title,程序员需要使用这个变量的话需要知道“HTML”
NSString *titleHTML;

//表示可变长的string
NSAttributedString *titleAttributedString;

//不需要更多的描述了
NSDate *now;

//告诉我们是一个日期,并且是上次修改时间的日期
NSDate *lastModifiedDate;

//单一个值可以用不同类表示时,变量名可以用来区分
NSURL *URL;
NSString *URLString;

//同样下面的名称,可以用来区分,这是一个string(表示date而已),而不是一个NSDate对象
NSString *releaseDateString;


另外,除非是单纯用于循环的技术变量,我们不建议使用当个字母作为变量名。

星号表示一个类型是一个指针,星号需要紧跟变量名。

比如:

NSString *text


而不是

NSString* text;

//或者
NSString * text;


除非在表示常量的情况下:

NSString * const NYTConstantString;


只要可行,就要使用属性定义,而不要使用单纯的实例变量。

应该尽量避免访问实例变量,除非是初始化方法(init,initWithCoder:等…),dealloc方法和自定义的getter和setter方法。

欲了解更多信息,请参阅苹果公司关于”using accessor methods in initializer methods and dealloc”的文档。

例如:

@interface NYTSection: NSObject

@property (nonatomic) NSString *headline;

@end


不要:

@interface NYTSection : NSObject {
NSString *headline;
}


变量修饰词

当用到由ARC引入的变量修饰法时,修饰符(__strong,__weak,__unsafe_unretained,__autoreleasing)应放在星号和变量名之间,例如:

NSString * __weak text;


命名

苹果的命名习惯应当尽可能遵守,特别是那些涉及到内存管理规则(NARC)的情况。

长而具备描述性的方法和变量名就不错。

例如:

UIButton *settingsButton;


而不该简单为:

UIButton *setBut;


类名和常量名必须加上三个字母的前缀(如,NYT),但是对于Core Data的实体名应该省略前缀。

常量为了表示清除,应该以相关类名为前缀,并且使用所有单词大写的驼峰规则命名。两个字母的前缀(如,NS)是保留给苹果使用的。

例如:

static const NSTimeInterval NYTArticleViewControllerNavigationFadeAnimationDuration = 0.3;


不要这样命名:

static const NSTimeInterval fadetime = 1.7;


属性和本地变量也应该是驼峰命名法,并且第一个词的首字母小写。

实例变量命名必须是驼峰的,并且第一个词的首字母小写,而且必须以下划线为前缀。这跟由LLVM自动合成的到的实例变量是一致的。如果LLVM能够自动合成该变量,那就让它自动合成。

例如:

@synthesize descriptiveVariableName = _descriptiveVariableName;


而不要:

id varnm;


Categories

建议使用Categories来简明扼要地划分功能,而且命名应该要能够描述该功能。

例如:

//用来播放媒体的功能
@interface UIViewController (NYTMediaPlaying)
//用来检测字符串的edcoding功能的
@interface NSString (NSStringEncodingDetection)


不应该这样:

@interface NYTAdvertisement (private)
@interface NSString (NYTAdditions)


在Categories中添加的方法以及属性,应该使用an app- or organization-specific prefix作为前缀命名。

(翻译不来:

Methods and properties added in categories MUST be named with an app-

or organization-specific prefix.



这样可以防止不小心覆盖了已有方法,并且能够减少来自两个不同库的Categories添加同一个名称的方法。(在后面这种情况,Objective-C运行时并不会指定调用哪个方法,这将导致无法预料的影响)

例如:

@interface NSArray (NYTAccessors)
- (id)nyt_objectOrNilAtIndex:(NSUInteger)index;
@end


不应该:

@interface NSArray (NYTAccessors)
- (id)objectOrNilAtIndex:(NSUInteger)index;
@end


注释

需要的时候,应该使用注释来解释为什么一段代码会做这样一些事情。使用的所有注释都需要保证是最新的,或者被删除的。

不建议使用块注释,因为我们写的代码应该尽量地如同文档一般可读,间歇性的,少数几行的注释就足够了。(当然这并不针对那些用于产生文档的注释)

init and dealloc方法

dealloc 方法应该放在@implementation的最上方,紧接着 @synthesize 和 @dynamic声明的地方。任何类的init方法都应该直接放在 dealloc方法的下面。

init 方法的结构应该如下:

- (instancetype)init {
self = [super init]; //或调用指定的初始化方法
if (self) {
// Custom initialization
}

return self;
}


Literals(简写)

在创建NSString, NSDictionary, NSArray, 和 NSNumber类的不可变对象时,使用它们的简写。要特别小心,不要将nil值传递给 NSArray 和 NSDictionary 因为这会导致crash。

比如:

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 *buildingZIPCode = [NSNumber numberWithInteger:10018];


CGRect 方法

当需要获取一个CGRect的x,y,width或者height时,代码必须使用CGGeometry方法,而不是直接获取结构体的成员。

从Apple’s CGGeometry reference有下面一段话:

All functions described in this reference that take CGRect data structures as inputs implicitly standardize those rectangles before calculating their results.

在该参考文献中所定义的所有用于获取CGRect数据结构作为inputs的方法,都在计算结果前预先标准化了那些rectangles。

For this reason, your applications should avoid directly reading and writing the data stored in the CGRect data structure.

因此,你的应用应该避免直接读写存储在CGRect数据结构体中的数据。

Instead, use the functions described here to manipulate rectangles and to retrieve their characteristics.

而应该使用这里的方法来操作rectangles并获取他们的属性值。

例如:

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;


常量

对于代码中的固定字符串或者数字,建议使用常量表示。因为它们便于通用,同时也便于修改(不用find/replace了)。

常量必须被定义为静态常量。

当常量明确被当作宏使用时,可以使用#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


枚举类型

当使用枚举时,必须使用新的确定基础类型的规范。它提供更强大的类型检查以及代码补全。SDK中有一个宏鼓励我们采用新的规范:NS_ENUM()

如:

typedef NS_ENUM(NSInteger, NYTAdRequestState) {
NYTAdRequestStateInactive,
NYTAdRequestStateLoading
};


Bitmasks--位掩码

当需要使用位掩码时,必须使用 NS_OPTIONS 宏.

如:

typedef NS_OPTIONS(NSUInteger, NYTAdCategory) {
NYTAdCategoryAutos      = 1 << 0,
NYTAdCategoryJobs       = 1 << 1,
NYTAdCategoryRealState  = 1 << 2,
NYTAdCategoryTechnology = 1 << 3
};


私有属性

私有属性应该在类的实现文件的类扩展(匿名Categories)中定义。

例如:

@interface NYTAdvertisement ()

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

@end


图片命名

To preserve organization and developer sanity,图片名称应该统一命名。图片名称应该使用驼峰字符串,它应该要能够描述图片的用途,接下来时它们描述的类的不加前缀的类名或者属性名,再然后加以颜色/位置,最后时它们的状态。

Images SHOULD be named as one camel case string with a description of their purpose, followed by the un-prefixed name of the class or property they are customizing (if there is one), followed by a further description of color and/or placement, and finally their state.

图片命名如下:

RefreshBarButtonItem / RefreshBarButtonItem@2x



RefreshBarButtonItemSelected / RefreshBarButtonItemSelected@2x

ArticleNavigationBarWhite / ArticleNavigationBarWhite@2x



ArticleNavigationBarBlackSelected / ArticleNavigationBarBlackSelected@2x.

被用于类似用途的图像应该被分组在一个图片文件夹或者Asset Catalog的特定的组中。

Booleans--布尔值

值不能直接与YES比较,因为YES的定义是 1。而Objective-C中的BOOL是CHAR类型,长8位(所以11111110直接和YES比较的话,或返回NO)。

对于对象指针:

if (!someObject) {
}

if (someObject == nil) {
}


对于一个 BOOL值:

if (isAwesome)
if (!someNumber.boolValue)
if (someNumber.boolValue == NO)


不要这样使用:

if (isAwesome == YES) // Never do this.


如果一个BOOL属性的名字被描述为一个形容词,那么这个属性的名字可以忽略is前缀,但是应该为它的getter方法指定符合习惯的名称:

例如:

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


Text and example taken from the Cocoa Naming Guidelines.

单例模式

单例对象应该使用一个线程安全的模式来创建它们的shared 实例。

+ (instancetype)sharedInstance {
static id sharedInstance = nil;

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[[self class] alloc] init];
});

return sharedInstance;
}


这能避免有可能并且有时候非常频繁的crash。

Imports操作

如果有不止一个import声明的话,这些声明需要进行分组。每组可以有注释。

注意:对modules使用 @import语法

// Frameworks
@import QuartzCore;

// Models
#import "NYTUser.h"

// Views
#import "NYTButton.h"
#import "NYTUserView.h"


Protocols--协议

在一个代理或者数据源协议中,每个方法的第一个参数都应该是消息的发送者。

当一个对象是许多类似类型对象的delegate时,这样可以避免混淆。并且也可以帮助告诉读者一个类正在实现这些代理方法。

例如:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;


不要这样:

- (void)didSelectTableRowAtIndexPath:(NSIndexPath *)indexPath;


Xcode 项目

为了避免文件杂乱无章地扩张,应该保证物理文件与Xcode的项目文件保持同步。Xcode创建的所有group都应该与文件系统中的文件夹相对应。为了看起来更清晰整洁,代码不仅需要通过类型分组,也需要通过功能分组。

Target Build Setting 中的“Treat Warnings as Errors”应该要开启。 开启尽可能多的additional warnings。如果你需要忽略特定某条warning,使用Clang’s pragma feature

其它编码规范:

Google

GitHub

Adium

Sam Soffes

CocoaDevCentral

Luke Redpath

Marcus Zarra

Wikimedia

我要醉了,这翻译有点难啊。←_←,必定是我英语太渣,直接翻译来整整五六个小时!!!而且有几条规范,还不是我的习惯诶。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  编码 文档