Objective-C编码规范

Tags: objective-c ios

介绍(Introduction)

我们编写这篇编码规范指南的目的是可以保持我们的书籍、教程和入门文章中的代码漂亮和一致 - 尽管我们的书籍有许多不同的作者合作。

The reason we made this style guide was so that we could keep the code in our books, tutorials, and starter kits nice and consistent - even though we have many different authors working on the books.

这篇Objective-C编码规范指南是与其他的你可能度过的Objective-C编码规范指南可能会不同,因为这篇规范的焦点集中在打印和网络的可读性。许多规范的确定着眼于节省打印空间,易读性和教程编写。

This style guide is different from other Objective-C style guides you may see, because the focus is centered on readability for print and the web. Many of the decisions were made with an eye toward conserving space for print, easy legibility, and tutorial writing.

语言(Language)

需要使用US English

US English should be used.

Preferred:

UIColor *myColor = [UIColor whiteColor];

Not Preferred:

UIColor *myColour = [UIColor whiteColor];

代码组织(Code Organization)

使用 #pragma mark 将方法按照功能以及协议/委托实现进行组织,参考下面的通用结构范本。

Use #pragma mark - to categorize methods in functional groupings and protocol/delegate implementations following this general structure.

#pragma mark - Lifecycle

- (instancetype)init {}
- (void)dealloc {}
- (void)viewDidLoad {}
- (void)viewWillAppear:(BOOL)animated {}
- (void)didReceiveMemoryWarning {}

#pragma mark - Custom Accessors

- (void)setCustomProperty:(id)value {}
- (id)customProperty {}

#pragma mark - IBActions

- (IBAction)submitData:(id)sender {}

#pragma mark - Public

- (void)publicMethod {}

#pragma mark - Private

- (void)privateMethod {}

#pragma mark - Protocol conformance
#pragma mark - UITextFieldDelegate
#pragma mark - UITableViewDataSource
#pragma mark - UITableViewDelegate

#pragma mark - NSCopying

- (id)copyWithZone:(NSZone *)zone {}

#pragma mark - NSObject

- (NSString *)description {}

空白(Spacing)

  • 缩进是用2个空格(这种方式节省空间,并尽可能减少换行),不要使用tab缩进,确保已在Xcode里面设定好相关偏好设置

    Indent using 2 spaces (this conserves space in print and makes line wrapping less likely). Never indent with tabs. Be sure to set this preference in Xcode.

  • 方法和其他代码(if/else/switch/while等等)的大括号总是在一行打开,在新行结束

  • Method braces and other braces (if/else/switch/while etc.) always open on the same line as the statement but close on a new line.

Preferred:

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

Not Preferred:

if (user.isHappy)
{
    //Do something
}
else {
    //Do something else
}
  • 为了方便阅读以及代码组织,需要在方法之间增加一个空行。方法内的空行用于区分不同的功能代码,但是如果方法中又太多的功能区块那么需要考虑重构代码。

    There should be exactly one blank line between methods to aid in visual clarity and organization. Whitespace within methods should separate functionality, but often there should probably be new methods.

  • 最好使用auto-synthesis。但是,如果有必要,@synthesize和@dynamic应各自在实现的新行定义。

    Prefer using auto-synthesis. But if necessary, @synthesize and @dynamic should each be declared on new lines in the implementation.

  • 通常不要使用冒号(:)对齐方法调用。但是有些情况下一个方法签名可能有>=3个冒号,使用冒号对齐可以使代码更易读。请不要使用冒号对齐含实现块的方法,因为Xcode的缩进会使代码更难读。

  • Colon-aligning method invocation should often be avoided.  There are cases where a method signature may have >= 3 colons and colon-aligning makes the code more readable. Please do NOT however colon align methods containing blocks because Xcode's indenting makes it illegible.

Preferred:

// blocks are easily readable
[UIView animateWithDuration:1.0 animations:^{
  // something
} completion:^(BOOL finished) {
  // something
}];

Not Preferred:

// colon-aligning makes the block indentation hard to read
[UIView animateWithDuration:1.0
                 animations:^{
                     // something
                 }
                 completion:^(BOOL finished) {
                     // something
                 }];

注释(Comments)

当需要注释时,注释需要描述一段代码的功能。所有注释需要保持更新,不需要的注释需要删除。

When they are needed, comments should be used to explain why a particular piece of code does something. Any comments that are used must be kept up-to-date or deleted.

通常情况不要写大块注释,尽量使代码可以自描述是用很少的几行注释即可。

Block comments should generally be avoided, as code should be as self-documenting as possible, with only the need for intermittent, few-line explanations.

例外情况:这个规则不适用于为生成文档而编写的注释。

Exception: This does not apply to those comments used to generate documentation.

命名(Naming)

应该遵守Apple的命名规则,尤其涉及到内存管理规则部分的命名规则。

Apple naming conventions should be adhered to wherever possible, especially those related to memory management rules (NARC).

长的具有描述性的方法和变量命名是比较好的。

Long, descriptive method and variable names are good.

Preferred:

UIButton *settingsButton;

Not Preferred:

UIButton *setBut;

类名和常量名需要有三个字符的前缀,对于核心数据的实体类则可以神略。在任何raywenderlich.com官方的书籍、入门文章和教程都会使用前缀'RWT'

A three letter prefix should always be used for class names and constants, however may be omitted for Core Data entity names. For any official raywenderlich.com books, starter kits, or tutorials, the prefix 'RWT' should be used.

常量应该使用驼峰格式(首字母大写)命名并且增加相关类的前缀。

Constants should be camel-case with all words capitalized and prefixed by the related class name for clarity.

Preferred:

static NSTimeInterval const RWTTutorialViewControllerNavigationFadeAnimationDuration = 0.3;

Not Preferred:

static NSTimeInterval const fadetime = 1.7;

属性应该使用驼峰格式命名(第一个字母小写)。为属性使用auto-synthesis,不要手工增加@synthesiz语句(除非你有更好的理由)。

Properties should be camel-case with the leading word being lowercase. Use auto-synthesis for properties rather than manual @synthesize statements unless you have good reason.

Preferred:

@property (strong, nonatomic) NSString *descriptiveVariableName;

Not Preferred:

id varnm;

下划线(Underscores)

当使用属性,实例变量时应该使用self.来访问,这意味着所有的属性将很容易区分,因为它们都使用 self. 开头。

When using properties, instance variables should always be accessed and mutated using self.. This means that all properties will be visually distinct, as they will all be prefaced with self..
一个例外:在初始化代码中的backing instance variable(如:_variableName)应直接使用,避免getter/setter方法中的任何潜在的问题。

An exception to this: inside initializers, the backing instance variable (i.e. _variableName) should be used directly to avoid any potential side effects of the getters/setters.
局部变量不应该包含下划线。

Local variables should not contain underscores.

方法(Methods)

在方法签名中需要在方法类型(-/+符号)后面增加一个空格。需要在方法段之间增加空白(符合苹果样式)。需包含关键字并且关键字需要能描述参数。关键字 and 是保留的,不要在多个参数中(如下面initWithWidth:height)所示使用and。

In method signatures, there should be a space after the method type (-/+ symbol). There should be a space between the method segments (matching Apple's style).  Always include a keyword and be descriptive with the word before the argument which describes the argument.

The usage of the word "and" is reserved.  It should not be used for multiple parameters as illustrated in the initWithWidth:height: example below.

Preferred:

- (void)setExampleText:(NSString *)text image:(UIImage *)image;
- (void)sendAction:(SEL)aSelector to:(id)anObject forAllCells:(BOOL)flag;
- (id)viewWithTag:(NSInteger)tag;
- (instancetype)initWithWidth:(CGFloat)width height:(CGFloat)height;

Not Preferred:

-(void)setT:(NSString *)text i:(UIImage *)image;
- (void)sendAction:(SEL)aSelector :(id)anObject :(BOOL)flag;
- (id)taggedView:(NSInteger)tag;
- (instancetype)initWithWidth:(CGFloat)width andHeight:(CGFloat)height;
- (instancetype)initWith:(int)width and:(int)height;  // Never do this.

变量(Variables)

变量命名需要尽可能的有意义。不要单字符命名变量(除了for循环中)。*号代表指针应该定义的靠近变量(常量例外),如:NSString *text, 不要用NSString* text 或者 NSString * text

Variables should be named as descriptively as possible. Single letter variable names should be avoided except in for() loops.

Asterisks indicating pointers belong with the variable, e.g., NSString *text not NSString* text or NSString * text, except in the case of constants.

Private properties should be used in place of instance variables whenever possible. Although using instance variables is a valid way of doing things, by agreeing to prefer properties our code will be more consistent.

Direct access to instance variables that 'back' properties should be avoided except in initializer methods (init, initWithCoder:, etc…), dealloc methods and within custom setters and getters. For more information on using Accessor Methods in Initializer Methods and dealloc, see here.

Preferred:

@interface RWTTutorial : NSObject

@property (strong, nonatomic) NSString *tutorialName;

@end

Not Preferred:

@interface RWTTutorial : NSObject {
  NSString *tutorialName;
}

Property Attributes

Property属性需要明确列出,这样有助于其他程序员阅读代码。属性顺序应该是storage然后是atomicity,这个顺序与代码生成器生成的代码顺序一致。

Property attributes should be explicitly listed, and will help new programmers when reading the code.  The order of properties should be storage then atomicity, which is consistent with automatically generated code when connecting UI elements from Interface Builder.

Preferred:

@property (weak, nonatomic) IBOutlet UIView *containerView;
@property (strong, nonatomic) NSString *tutorialName;

Not Preferred:

@property (nonatomic, weak) IBOutlet UIView *containerView;
@property (nonatomic) NSString *tutorialName;

易变的属性需要使用copy来代替strong。为什么?如果你声明了一个NSString的属性,另外一个人或许会将他放到NSMutableString的实例,这样就会改变你原先的NSString。

Properties with mutable counterparts (e.g. NSString) should prefer copy instead of strong. Why? Even if you declared a property as NSString somebody might pass in an instance of an NSMutableString and then change it without you noticing that.

Preferred:

@property (copy, nonatomic) NSString *tutorialName;

Not Preferred:

@property (strong, nonatomic) NSString *tutorialName;

点标记语法(Dot-Notation Syntax)

点语发是使用访问控制方法的易用包装,当你使用点语法时,属性的访问和设置将会通过gettet和setter方法进行。

Dot syntax is purely a convenient wrapper around accessor method calls. When you use dot syntax, the property is still accessed or changed using getter and setter methods.  Read more here

当访问和修改属性是应该总是使用点语法,这会使得代码更简洁。括号符号具有更高的优先级。

Dot-notation should always be used for accessing and mutating properties, as it makes code more concise. Bracket notation is preferred in all other instances.

Preferred:

NSInteger arrayCount = [self.array count];
view.backgroundColor = [UIColor orangeColor];
[UIApplication sharedApplication].delegate;

Not Preferred:

NSInteger arrayCount = self.array.count;
[view setBackgroundColor:[UIColor orangeColor]];
UIApplication.sharedApplication.delegate;

文字(Literals)

创建不易变类型实例是应该使用NSString, NSDictionary, NSArray, and NSNumber。要特别小心,nil值不能被传递到的NSArray和NSDictionary中,因为这会导致崩溃。

NSString, NSDictionary, NSArray, and NSNumber literals should be used whenever creating immutable instances of those objects. Pay special care that nil values can not be passed into NSArray and NSDictionary literals, as this will cause a crash.

Preferred:

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

Not Preferred:

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

常量(Constants)

常量优于内联字符串文字或数字,因为它们允许容易地表示常用变量,并且可以被快速改变而无需查找和替换。除非明确地被用作宏(使用#defines定义),常量应声明为静态的。

Constants are preferred over in-line string literals or numbers, as they allow for easy reproduction of commonly used variables and can be quickly changed without the need for find and replace. Constants should be declared as static constants and not #defines unless explicitly being used as a macro.

Preferred:

static NSString * const RWTAboutViewControllerCompanyName = @"RayWenderlich.com";

static CGFloat const RWTImageThumbnailHeight = 50.0;

Not Preferred:

#define CompanyName @"RayWenderlich.com"

#define thumbnailHeight 2

枚举类型(Enumerated Types)

当使用枚举时,建议使用新的固定的基本类型规范,因为它具有更强的类型检查和代码完成。SDK现在包括一个宏来促进和鼓励使用固定的基本类型: NS_ENUM()

When using enums, it is recommended to use the new fixed underlying type specification because it has stronger type checking and code completion. The SDK now includes a macro to facilitate and encourage use of fixed underlying types: NS_ENUM()

For Example:

typedef NS_ENUM(NSInteger, RWTLeftMenuTopItemType) {
  RWTLeftMenuTopItemMain,
  RWTLeftMenuTopItemShows,
  RWTLeftMenuTopItemSchedule
};

您也可以进行明确赋值(老式的k-style常量定义)

You can also make explicit value assignments (showing older k-style constant definition):

typedef NS_ENUM(NSInteger, RWTGlobalConstants) {
  RWTPinSizeMin = 1,
  RWTPinSizeMax = 5,
  RWTPinCountMin = 100,
  RWTPinCountMax = 500,
};

旧的k-style常量定义应该避免使用除非你在编写核心的C代码。

Older k-style constant definitions should be avoided unless writing CoreFoundation C code (unlikely).

Not Preferred:

enum GlobalConstants {
  kMaxPinSize = 5,
  kMaxPinCount = 500,
};

Case语句(Case Statements)

case语句不要使用括号,除非编译器提示。当case包含超过一行语句时,需要增加括号。

Braces are not required for case statements, unless enforced by the complier.
When a case contains more than one line, braces should be added.

switch (condition) {
  case 1:
    // ...
    break;
  case 2: {
    // ...
    // Multi-line example using braces
    break;
  }
  case 3:
    // ...
    break;
  default: 
    // ...
    break;
}

有些情况下,相同的代码可以适用于多个case以及下方的case语句,可以去掉case后的break语句允许执行下方case代码,这种语句需要进行注释来保证代码的清晰。

There are times when the same code can be used for multiple cases, and a fall-through should be used.  A fall-through is the removal of the 'break' statement for a case thus allowing the flow of execution to pass to the next case value.  A fall-through should be commented for coding clarity.

switch (condition) {
  case 1:
    // ** fall-through! **
  case 2:
    // code executed for values 1 and 2
    break;
  default: 
    // ...
    break;
}

在swithc中使用enum时,不需要default段

When using an enumerated type for a switch, 'default' is not needed.   For example:

RWTLeftMenuTopItemType menuType = RWTLeftMenuTopItemMain;

switch (menuType) {
  case RWTLeftMenuTopItemMain:
    // ...
    break;
  case RWTLeftMenuTopItemShows:
    // ...
    break;
  case RWTLeftMenuTopItemSchedule:
    // ...
    break;
}

私有属性(Private Properties)

私有属性应该在类实现文件的扩展(匿名类)中声明。命名类别(如RWTPrivate或private)不应该被使用,除非扩展另一个类。匿名类可以共享/暴露使用+Private.h文件命名约定检验。

Private properties should be declared in class extensions (anonymous categories) in the implementation file of a class. Named categories (such as RWTPrivate or private) should never be used unless extending another class.   The Anonymous category can be shared/exposed for testing using the +Private.h file naming convention.

For Example:

@interface RWTDetailViewController ()

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

@end

布尔值(Booleans)

Objective-C使用 YES 和 NO。因此 true 和 false 只能在用于CoreFoundation的 C 或 C++代码中。因为nil被解析为NO所以nil不需要在条件中比较它。从不直接比较YES,因为YES是被定义为1,而一个布尔值可多达8位。

Objective-C uses YES and NO.  Therefore true and false should only be used for CoreFoundation, C or C++ code.  Since nil resolves to NO it is unnecessary to compare it in conditions. Never compare something directly to YES, because YES is defined to 1 and a BOOL can be up to 8 bits.

这使得文件有更好的可读性和一致性。

This allows for more consistency across files and greater visual clarity.

Preferred:

if (someObject) {}
if (![anotherObject boolValue]) {}

Not Preferred:

if (someObject == nil) {}
if ([anotherObject boolValue] == NO) {}
if (isAwesome == YES) {} // Never do this.
if (isAwesome == true) {} // Never do this.

如果BOOL属性的名称表示为一个形容词,该属性可以省略“is”字头,但需要为get方法按照常规指定名称,例如:

If the name of a BOOL property is expressed as an adjective, the property can omit the “is” prefix but specifies the conventional name for the get accessor, for example:

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

Text and example taken from the Cocoa Naming Guidelines.

条件语句(Conditionals)

条件体应当始终需要用括号以避免错误,即使条件体可以不用括号(比如仅一行的条件语句)。这些错误包括增加第二行代码,并期待它是if语句的一部分。另一个更危险的缺陷可能发生的地方就行“内部”的if语句被注释掉,并使得下一行不知不觉成为if语句的一部分。此外,这种风格是与所有其他条件句更一致的,因此更容易可扫描。

Conditional bodies should always use braces even when a conditional body could be written without braces (e.g., it is one line only) to prevent errors. These errors include adding a second line and expecting it to be part of the if-statement. Another, even more dangerous defect may happen where the line "inside" the if-statement is commented out, and the next line unwittingly becomes part of the if-statement. In addition, this style is more consistent with all other conditionals, and therefore more easily scannable.

Preferred:

if (!error) {
  return success;
}

Not Preferred:

if (!error)
  return success;

or

if (!error) return success;

三元运算符(Ternary Operator)

仅仅当为了提高代码可读性和整洁性的时候才可以使用三元运算符,通常是一个单一的条件。多个条件通常使用更易于理解的if语句或重构到实例变量。在一般情况下,三元运算的最佳使用场景是根据条件为变量赋值。

The Ternary operator, ?: , should only be used when it increases clarity or code neatness. A single condition is usually all that should be evaluated. Evaluating multiple conditions is usually more understandable as an if statement, or refactored into instance variables. In general, the best use of the ternary operator is during assignment of a variable and deciding which value to use.

布尔变量比较时应该使用否 != 比较需要增加括号,以提高可读性。如果变量是一个布尔类型,那么没有必要添加括号。

Non-boolean variables should be compared against something, and parentheses are added for improved readability.  If the variable being compared is a boolean type, then no parentheses are needed.

Preferred:

NSInteger value = 5;
result = (value != 0) ? x : y;

BOOL isHorizontal = YES;
result = isHorizontal ? x : y;

Not Preferred:

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

初始化方法(Init Methods)

初始化方法依照Apple的通用代码模板即可。返回类型为instancetpye时应该使用返回id。

Init methods should follow the convention provided by Apple's generated code template.  A return type of 'instancetype' should also be used instead of 'id'.

- (instancetype)init {
  self = [super init];
  if (self) {
    // ...
  }
  return self;
}

See Class Constructor Methods for link to article on instancetype.

类构造器方法(Class Constructor Methods)

当类构造器方法被调用后,返回instancetype类型不要返回id。这确保编译器能够正确推断结果类型。

Where class constructor methods are used, these should always return type of 'instancetype' and never 'id'. This ensures the compiler correctly infers the result type.

@interface Airplane
+ (instancetype)airplaneWithType:(RWTAirplaneType)type;
@end

More information on instancetype can be found on NSHipster.com.

CGRect函数(CGRect Functions)

当访问CGRect的x,y,width,height属性时,需要使用CGGeometry 函数 而不要直接访问结构成员。参考apple 的CGGeometry参考:

When accessing the x, y, width, or height of a CGRect, always use the CGGeometry functions instead of direct struct member access. From 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. For this reason, your applications should avoid directly reading and writing the data stored in the CGRect data structure. Instead, use the functions described here to manipulate rectangles and to retrieve their characteristics.

Preferred:

CGRect frame = self.view.frame;

CGFloat x = CGRectGetMinX(frame);
CGFloat y = CGRectGetMinY(frame);
CGFloat width = CGRectGetWidth(frame);
CGFloat height = CGRectGetHeight(frame);
CGRect frame = CGRectMake(0.0, 0.0, width, height);

Not Preferred:

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;
CGRect frame = (CGRect){ .origin = CGPointZero, .size = frame.size };

Golden Path

当编写条件语句代码,不能嵌套if语句。多个return语句都OK。

When coding with conditionals, the left hand margin of the code should be the "golden" or "happy" path.  That is, don't nest if statements.  Multiple return statements are OK.

Preferred:

- (void)someMethod {
  if (![someOther boolValue]) {
    return;
  }

  //Do something important
}

Not Preferred:

- (void)someMethod {
  if ([someOther boolValue]) {
    //Do something important
  }
}

错误处理(Error handling)

When methods return an error parameter by reference, switch on the returned value, not the error variable.

Preferred:

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

Not Preferred:

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

Some of Apple’s APIs write garbage values to the error parameter (if non-NULL) in successful cases, so switching on the error can cause false negatives (and subsequently crash).

Singletons

Singleton objects should use a thread-safe pattern for creating their shared instance.

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

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

  return sharedInstance;
}

This will prevent possible and sometimes prolific crashes.

Line Breaks

Line breaks are an important topic since this style guide is focused for print and online readability.

For example:

self.productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:productIdentifiers];

A long line of code like this should be carried on to the second line adhering to this style guide's Spacing section (two spaces).

self.productsRequest = [[SKProductsRequest alloc] 
  initWithProductIdentifiers:productIdentifiers];

Smiley Face

Smiley faces are a very prominent style feature of the raywenderlich.com site!  It is very important to have the correct smile signifying the immense amount of happiness and excitement for the coding topic.  The end square bracket is used because it represents the largest smile able to be captured using ascii art.  A half-hearted smile is represented if an end parenthesis is used, and thus not preferred.

Preferred:

:]

Not Preferred:

:)

Xcode project

The physical files should be kept in sync with the Xcode project files in order to avoid file sprawl. Any Xcode groups created should be reflected by folders in the filesystem. Code should be grouped not only by type, but also by feature for greater clarity.

When possible, always turn on "Treat Warnings as Errors" in the target's Build Settings and enable as many additional warnings as possible. If you need to ignore a specific warning, use Clang's pragma feature.

Other Objective-C Style Guides

If ours doesn't fit your tastes, have a look at some other style guides:



本文链接:http://www.4byte.cn/learning/120004/objective-c-bian-ma-gui-fan.html