Objective-C style guide

分類:技術 時間:2016-10-25

Introduction

這個style guide規范描述了我們iOS開發團隊喜歡的Objectiv-C編程習慣。

代碼規范的意義,在于提高團隊各個成員寫的代碼的一致性和可讀性。一致性能減少工程師編寫代碼風格的困惑和猶豫;良好的可讀性更是能幫助我們瀏覽其他人代碼、以便合作和code review。

它并不是去定義the right way or the wrong way,而是達成一個需要遵守的共識,it's about agreeing on doing things the same way。

這個規范可能比其他一般的style guide規范涵蓋了更多的方面和細節。

歡迎聯系我,給出你關于這個規范的想法。

Credits

它絕大部分內容,來自 Wonderful Objective-C style guide ,并加入了自己的一些偏好。

Background

此規范也參考了一些蘋果官方文檔。如果在此規范里找不到你關心的內容,可以嘗試在下面的文檔里尋找:

Tools

VVDocumenter-Xcode

VVDocumenter-Xcode 是一個幫我們寫注釋(用于生成文檔)的Xcode插件。可以使用 Alcatraz Xcode package manager 安裝后重啟Xcode。

clang-format

  1. Download clang-format 3.5.2
  2. Copy clang-format into folder /usr/local/bin/
  3. Copy pre-commit into folder .gits/hooks/
  4. chmod x pre-commit
  5. Change .clang-format if you want to change the style.

Cocoapods

當需要一些“輪子”時,可以先在現有的代碼里面找找(Podfile或者Libraray)。沒有的話,再用pod search或者github找找,看有沒有代碼質量高的、大家廣泛使用的(watch、star、fork數量多)、比較活躍、比較友好的第三方代碼,看看實現原理以及使用方法,是否適合我們的需求。

或可以通過繼承,滿足我們的需求。如果第三方的代碼有bug,或者需要改進,可以自己fork后修改使用(順便可以給作者發一個pull request)

在你有更好的實現、有時間的前提下,可以自己造更好的“輪子”。

Podfile.lock 有變動表示使用的第三庫版本有變化,如果采用新的版本,需要檢查新版本的第三方庫沒有問題。

盡量使用最新版本的第三方庫。

但發包前對此文件的修改要多尤其小心。最好發完包后,自己看看新版本有無問題,如果有必要再讓測試看看。

Cocoapods命令解釋

  • pod update: Updating local specs repositories, 更新工程中的第三方庫
  • pod update --no-repo-update:使用local specs repositories中最新的版本
  • pod install: Updating local specs repositories, 安裝podfile.lock中規定版本
  • pod install --no-repo-update:當podfile.lock中規定的版本,不在local specs repositories中,install會失敗

Cocoapods使用規范

  • pod update命令,由小組某個成員,開啟定時任務:
    30 12 1 cd /Users/Can/Workspace/zuche;pod update;
    //用來檢查第三方庫是否有更新,并升級庫。這個是為了定期檢查第三方庫是否有新版本。
    //一個小組成員做這件事就可以
    //發現Podfile.lock變了,表示第三方代碼版本升級了,應該了解一下升級的內容,另外上線前不要輕易升級影響功能第三方代碼
  • pod install命令,由小組其他成員,開啟定時任務:
    30 12 * cd /Users/Can/Workspace/zuche;pod install;
    //主要用來Updating local specs repositories,防止install失敗
  • pull完代碼推薦使用pod install,pod install --no-repo-update,后者速度更快可能會報錯
  • git checkout 老代碼,推薦使用pod install --no-repo-update,因為是老代碼,一般不會報錯
  • Podfile.lock任何情況下不能刪除
  • pod update --no-repo-update,只用在pod install不成功,但需要馬上編譯成功的情況

Table of Contents

  • Line Wrapping (Code Width)
    • Constant Documentation
    • Property Documentation
    • Class Method Documentation
    • Instance Method Documentation
    • Naming Conventions for Methods and Variables
    • Naming Conventions for Constants and Macros
    • Naming Conventions for Enumerated Types
  • Switch Statements and Case Label Blocks
    • Conditional Expressions
  • Class Constructor Methods
  • Other Objective-C Style Guides

Language

使用美國英語,而非英國等國家英語。

Preferred:

UIColor *myColor = [UIColor whiteColor];

Not Preferred:

UIColor *myColour = [UIColor whiteColor];

Project Organization

Xcode工程,采用類似下面的工程文件結構。

Your_Project
  |-- AppDelegate.h
  |-- AppDelegate.m
  |-- Images.xcassets
  |-- MainMenu.xib
  |-- Supporting Files
  |-- Models
  |-- Views
  |-- Controllers
  |-- Stores
  |-- Helpers
  |-- ...

并且保持對應的物理文件系統路徑,方便在finder里面查找,也能保持finder內文件整齊。因此需要加入新的group時,需要先在文件系統里新建一個folder,然后add這個folder。

Code Organization

使用 #pragma mark - 對.m文件里面的Methods進行歸類。

#pragma mark - Class Methods

  (ClassObject)classWithDefaults;
  (ClassObject)convertToJSON;
  (ClassObject)convertToXML;

#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 {}

line-wrapping-(code-width)

合適的規范一般并不需要一行能容納無限長度的代碼,80列的限制反而能提醒你,可能你的代碼可讀性不太好。

Preferences-gt;Text Editing-gt;Page Guide at column:

As seen here:

Xcode Page Guide Pref

Spacing

  • 縮進使用4個空格(Xcode默認)。
  • 操作符左右留空格。

Preferred:

if (self.changeEable amp;amp; self.shouldChange)
sum = count1   count2

Not Preferred:

if (self.changeEableamp;amp;self.shouldChange)
sum=count1 count2
  • 方法體的花括號需要在新的一行開啟,在新的一行關閉 。而其它花括號( if / else / switch / while etc.),加入一個空格后在行尾開啟,在新一行關閉(Xcode默認)。

Preferred:

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

Not Preferred:

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

在使用 else 或者 else if ,它們前后的大括號應該是在一行,否則不太好看:

Not Preferred:

if (user.isHappy) 
{
  // Do something
} 
else 
{
  // Do something else
}
  • methods之間只留一個空行。
  • 盡量使用auto-synthesis。如果需要使用 @synthesize ,每個property需要新開一行, @dynamic 也是需要新開一行。
  • 當methods里面需要傳入block時,不要使用冒號對齊對方式:

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

如果需要注釋,它只用來解釋 為什么 這段代碼要這么寫,并且是正確的,代碼變化時也需要馬上更新,不能有誤導。

盡可能避免大量使用注釋,好的代碼應該盡可能是self-documenting。

Exception: 上面兩條不適用于生成文檔用的注釋.

Documentation

在頭文件中的以下內容,需要寫注釋用來生成文檔(可以使用appledoc)。

VVDocumenter-Xcode 也可以幫我們。

  • Classes
  • Prototype
  • Constants
  • Properties
  • Class Methods
  • Instance Methods

Class Documentation

-- **EXAMPLE PENDING** --

Constant Documentation

/**
 *  @memberof PDFReaderConfig
 *  Default value for bookmarksEnabled: TRUE
 */
extern const BOOL kPDFReaderDefaultBookmarksEnabled;

Property Documentation

/**
 *  Enable/disable bookmark support.
 *
 *  @see kPDFReaderDefaultBookmarksEnabled
 */
@property (nonatomic, readwrite, unsafe_unretained, getter=isBookmarksEnabled)
    BOOL bookmarksEnabled;

Class Method Documentation

/**
 * Returns the shared PDFReaderConfig instance, creating it if necessary.
 *
 * @return The shared PDFReaderConfig instance.
 */
  (instancetype)sharedConfig;

Instance Method Documentation

/**
 *  Initializes and returns a newly allocated PDFReaderViewController object.
 *
 *  @param object Reference to an initialized PDFReaderDocument
 *
 *  @return Initialized class instance or nil on failure
 *
 *  @throws quot;lt;MissingArgumentsgt;quot; When object is nil
 *  @throws quot;lt;WrongTypegt;quot; When object is not a reference to a PDFReaderDocument
 *    object
 *
 *  @remark Designated initializer.
 */
- (instancetype)initWithReaderDocument:(PDFReaderDocument *)object;

/**
 *  Update bookmark state for each page in the PDFReaderDocument's bookmarks 
 *    list.
 */
- (void)updateToolbarBookmarkIcon;

Naming

Naming Conventions for Methods and Variables

詳盡的、描述性的方法和變量名,對代碼的self-documenting是很有幫助的。命名的清晰性和簡潔性都很重要,然而,在Objective-C的世界里,不能為簡潔性犧牲清晰性。

Preferred:

UIButton *settingsButton;
NSString *pageTitle = @quot;My Titlequot;;
int pageCounter = 0;

Not Preferred:

UIButton *setBut;
NSString *string = @quot;My Titlequot;;
int c = 0;

Naming Conventions for Constants and Macros

下面的命名方式第一眼望去可能覺得好復雜,習慣習慣了就好了,別偷懶。。。

tPRE_Space_Name

Where:

Element Definition Value Example
t type = constant k* k MIX_MyClass_DefaultTitle
type = macro m* m BUZ_MyClass_doubleIt
PRE three-letter prefix XXX k XXX _MyClass_MenuBarHeight
Space unique namespace within tPRE WindowSize kXXX_ MyClass _WindowSize
Name unique name within tPRE_Space BorderColor kZRE_MyClass_ BorderColor
isIphone mZBB_MyClass_ isIphone

類型(t)元素要么是quot;kquot; (for constant),要么是quot;mquot; (for macro)。

PRE元素一般是公司名稱前三個字母的大寫。

Space元素可以是Library名、類名、app名稱或模塊名稱,采用駝峰命名法并首字母大寫。

Name元素是具體名稱, constant 采用駝峰命名法并首字母 寫, macro 采用駝峰命名法并首字母 寫。

Preferred:

static const NSInteger kMIX_PDFReader_DefaultPagingViews = 3;

Not Preferred:

static NSTimeInterval const fadetime = 1.7;

Preferred

#define mMIX_MacroLib_rgb(r,g,b) [UIColor colorWithRed:r/255.0f green:g/255.0f blue:b/255.0f alpha:1.0f]

Not Preferred

#define RGBColor(r,g,b) [UIColor colorWithRed:r/255.0f green:g/255.0f blue:b/255.0f alpha:1.0f]

類的成員變量采用駝峰命名法并首字母 寫。

Preferred:

@property (nonatomic, readwrite, strong) NSString *descriptiveVariableName;

// if/when needed
@synthesize descriptiveVariableName = _descriptiveVariableName;

Not Preferred:

id varnm;
...
@property (strong) id varnm;
...
@synthesize varnm;

Naming Conventions for Enumerated Types

Enumerated Type names用e開頭,其他和Constants一樣。

Preferred:

// using the NS_ENUM macro (preferred)
typedef NS_ENUM(NSInteger, eMIX_UtilityLib_PlayerStateType) {
  PlayerStateOff,
  PlayerStatePlaying,
  PlayerStatePaused
};

Not Preferred:

enum {
    PlayerStateOff,
    PlayerStatePlaying,
    PlayerStatePaused
};

typedef NSInteger PlayerState;

Underscores

讀或寫所有properties,instance variables,都應該用 self. ,除了以下三種列外情況:

  • Setup and Tear down: init and dealloc
  • Overriding accessors (getters/setters)
  • Archiving activities: e.g. NSCoding Protocol's encodeWithCoder and initWithCoder

當一個對象還沒有初始化好,它處于不穩定的狀態,調用getters/setters的副作用,可能會導致異常情況發生,所以應該直接使用 _variableName 。see: Don’t Message self in Objective-C init (or dealloc)

重載getter/setter來添加自定義的邏輯,必須使用 _variableName ,否則會無限循環。

歸檔或使用其它持久化方法時,使用 _variableName 可以避免getters/setters的副作用對屬性的改變。比如你使用getter讀一個屬性時,里面的邏輯可能修改了其他屬性的值。

還有,local variables不能使用下劃線。

Methods

方法名的-/ 符號后面要留一個空格,冒號前面的描述詞不能省略,方法里面不要使用and。

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* textNSString * text

如果存在qualifier,格式應該為 ClassName * qualifier variableName;

為了一致性,類的成員變量應該使用 property

Preferred:

@interface MyAppTutorial : NSObject

@property (strong, nonatomic) NSString *tutorialName;

@end

Not Preferred:

@interface MyAppTutorial : NSObject {
  NSString *tutorialName;
}

Property Attributes

property的 所有 屬性,應該按照atomicity、accessibility(readonly, readwrite)、storage的順序明確列出來。

Preferred:

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

Not Preferred:

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

一個非容器類,如果它具有mutable的相關類,比如NSString與NSMutableString、NSURLRequest與NSMutableURLRequest,在定義類成員變量時,應該使用 copy 而不是 strong 。why?當你定義NSSting為strong時,它可能被賦值為NSMutableString類型對象,這個對象可能會在你不注意的情況下改變值,而你既然把它類型聲明為NSSting,這種情況顯然不是你需要的。

Preferred:

@property (nonatomic, readwrite, copy) NSString *tutorialName;

Not Preferred:

@property (nonatomic, readwrite, strong) NSString *tutorialName;

Dot-Notation Syntax

前面講Underscores時,說了下點語法的使用。

還有一個限制是,點語法只應該用在訪問對象成員變量,而不是去調用對象的方法:

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

盡可能使用語法糖去創建immutable對象。

Preferred:

NSArray *names = @[@quot;Brianquot;, @quot;Mattquot;, @quot;Chrisquot;, @quot;Alexquot;, @quot;Stevequot;, @quot;Paulquot;];
NSDictionary *productManagers = @{@quot;iPhonequot;: @quot;Katequot;, @quot;iPadquot;: @quot;Kamalquot;, @quot;Mobile Webquot;: @quot;Billquot;};
NSNumber *shouldUseLiterals = @YES;
NSNumber *buildingStreetNumber = @10018;

Not Preferred:

NSArray *names = [NSArray arrayWithObjects:@quot;Brianquot;, @quot;Mattquot;, @quot;Chrisquot;, @quot;Alexquot;, @quot;Stevequot;, @quot;Paulquot;, nil];
NSDictionary *productManagers = [NSDictionary dictionaryWithObjectsAndKeys: @quot;Katequot;, @quot;iPhonequot;, @quot;Kamalquot;, @quot;iPadquot;, @quot;Billquot;, @quot;Mobile Webquot;, nil];
NSNumber *shouldUseLiterals = [NSNumber numberWithBool:YES];
NSNumber *buildingStreetNumber = [NSNumber numberWithInteger:10018];

Constants

因為類型安全的緣故,常量不應該用 #define 去定義,應該聲明為全局變量。

Preferred:

// Format: type const constantName = value;
NSString * const kMIX_MyClassShortDateFormat = @quot;MM/dd/yyyyquot;;

// Macro: #define constantName value
\#define mMIX_MacroLib_isIPad (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)

Not Preferred:

\#define CompanyName @quot;MyCompanyquot;
\#define thumbnailHeight 2

為了使全局變量能夠在其他文件類使用,可以在 .h 中加入:

extern NSString * const kMIX_MyClass_ShortDateFormat;

如果這個全局變量只用在當前類里面,需要在 .m 中去定義,并且前面加上 static 關鍵字限定作用域。

static NSString * const kMIX_MyClass_ShortDateFormat = @quot;MM/dd/yyyyquot;;

一個static變量如果定義在函數體里面,它的值能夠一直保存,下次調用這個函數時,它的值還是上次調用這個函數后的值。

Enumerated Types

使用 NS_ENUM() 去定義枚舉,它能夠讓編譯器檢查枚舉的數據類型。在swift里使用Objecive-C的枚舉,也是需要使用 NS_ENUM() 才可以。

Preferred

// using the NS_ENUM macro (preferred)
typedef NS_ENUM(NSInteger, eMIX_UtilityLib_PlayerStateType) {
  PlayerStateOff,
  PlayerStatePlaying,
  PlayerStatePaused
};

// with explicit value assignments
typedef NS_ENUM(NSInteger, eMIX_UtilityLib_PlayerStateType) {
  PlayerStateOff = 0,
  PlayerStatePlaying = 1,
  PlayerStatePaused = 2
};

Not Preferred:

// Apple Modern Objective-C style, provides type checking, enum type declared inline...
typedef enum _eMIX_UtilityLib_PlayerStateType : NSUInteger {
    PlayerStateOff,
    PlayerStatePlaying,
    PlayerStatePaused
} eMIX_UtilityLib_PlayerStateType;

// Apple Modern Objective-C style, provides type checking, enum type declared seperately...
enum _eMIX_UtilityLib_PlayerStateType : NSInteger {
  PlayerStateOff,
  PlayerStatePlaying,
  PlayerStatePaused
};
typedef enum _eMIX_UtilityLib_PlayerStateType : NSInteger eMIX_UtilityLib_PlayerStateType;

// Apple legacy style enum (32bit/64bit portability, no formal relationship between type and enum)...
enum {
    PlayerStateOff,
    PlayerStatePlaying,
    PlayerStatePaused
};
typedef NSInteger PlayerState;

// Apple legacy style enum (no 32bit/64bit portability)...
typedef enum {
    PlayerStateOff,
    PlayerStatePlaying,
    PlayerStatePaused
} PlayerState;
// NOTE: Implies...
// typedef int PlayerState;

// Generic C-style enum (no 32bit/64bit portability)...
typedef enum {
    PlayerStateOff,
    PlayerStatePlaying,
    PlayerStatePaused
} PlayerState;

Switch Statements and Case Label Blocks

前面說過, switich: 后面的大括號不應該新開一行。

case: 后面一般是不用帶大括號的,但如果你需要在里面聲明臨時變量,編譯器要求必須要帶大括號。為了一致性和可讀性,當case后面的語句多于一行時,帶上大括號。

switch (condition) {
  case 1:
    // ...
    break;

  case 2: {
    // ...
    // Multi-line example using braces
    break;
  }

  case 3:
    // ...
    break;

   default: 
    // ...
    break;
}

當多個 case: 需要執行的代碼一樣時,需要去掉 break ,并注釋 fall-through!

switch (condition) {
  case 1:
    // ** fall-through! **

  case 2:
    // code executed for values 1 and 2
    break;

  default: 
    // ...
    break;
}

當使用枚舉時, default 是多余的,不用寫。每個枚舉值都需要覆蓋到,否則會有警告。

kMIX_LeftMenuTopItemType menuType = kMIX_LeftMenuTopItemMain;

switch (menuType) {
  case kMIX_LeftMenuTopItemMain:
    // ...
    break;
  case kMIX_LeftMenuTopItemShows:
    // ...
    break;
  case kMIX_LeftMenuTopItemSchedule:
    // ...
    break;
}

Private Properties

類私有的變量應該聲明在class extensions (anonymous categories)中。它可以放在 .m 文件里,或者新建一個 lt;headerfilegt; Private.h文件,給子類或測試用。

For Example:

@interface MyAppDetailViewController ()

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

@end

Image Naming

圖片放入Images.xassets,命名方式是首字母大寫的駝峰命名法,并將不同的部分往后靠,在命名中盡量加入模塊名、類名進行區分。比如ShortRentCarRentButtonNormal,ShortRentCarRentButtonTouched。

Examples:

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

Booleans

Objective-C使用 YESNO ,而不是 truefalse (在CoreFoundation,C或C 代碼里可以使用)。

nil可以表示 NO ,所以在 if 中,對象的值與 NO 比較是沒必要的。

if 中不要任何值與 YESNO 比較, YES 是#define YES (BOOL)1,而 BOOL 是8bit。

Preferred:

if (someObject) {
  // code...
}

if (![anotherObject boolValue]) {
  // code...
}

Not Preferred:

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

如果一個類的成員變量類型是 BOOL ,并且它可以是個形容詞XXX,那么 is 的前綴可以省略,但需要加 getter=isXXX

@property (nonatomic, readwrite, unsafe_unretained, getter=isEditable) BOOL editable;

參見 Cocoa Naming Guidelines .

Conditionals

為了避免錯誤、方便以后增加代碼、一致性和可讀性, if 后面的大括號應該一直有,大括號在 if 所在行開啟。避免的錯誤包括誤將 if 大括號代碼塊里面的內容弄到 if 外面去了,還有 even more dangerous defect

Preferred:

if (!error) {
  return success;
}

Not Preferred:

if (!error)
  return success;

or

if (!error) return success;

Conditional Expressions

if 小括號里面的表達式,應該是一個變量,而不是一個函數調用,在 if 小括號里包含太多邏輯不太容易引起人注意。但類似 [anObject boolValue] 這種只會讀值,不會有其他邏輯的,是一個例外,但anObject的變量名也需要足夠清楚,這種情況單獨再聲明一個bool型變量不會增加可讀性。

Preferred:

BOOL continuousPlayEnabled = [[MediaAppPrefs sharedInstance] continuousPlay];
MediaAppTrack *nextMediaTrack = [MediaAppPlayer nextTrack];
if (continuousPlayEnabled amp;amp; nextMediaTrack) {
  // play the next song
}

Not Preferred:

if ([[MediaAppPrefs sharedInstance] continuousPlay] amp;amp; [MediaAppPlayer nextTrack]) {
  // play the next song
}

Ternary Operator

三元操作符號不可濫用,導致可讀性降低。

Preferred:

NSInteger value = http://www.tuicool.com/articles/5;
result = (value != 0) ? x : y;

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

Not Preferred:

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

result = isHorizontal ? x : y;

Return Statements

不要 return 一個函數調用。

不要在函數中間調用 return ,可以在函數前面檢查傳參合法性時使用 return 。對于一個比較長的函數,中間有個 return ,讀代碼的人很難找到。

Preferred:

- (BOOL) playNext
{
  BOOL continuousPlayEnabled = [[MediaAppPrefs sharedInstance] continuousPlay];
  MediaAppTrack *nextMediaTrack = [MediaAppPlayer nextTrack];

  return (continuousPlayEnabled amp;amp; nextMediaTrack);
}

Not Preferred:

- (BOOL) playNext
{
  return ([[MediaAppPrefs sharedInstance] continuousPlay] amp;amp; [MediaAppPlayer nextTrack]);
}

Init Methods

初始化方法返回類型應該為 instancetype ,而不是 id

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

  return self;
}

參見 NSHipster.com

另外,應該定義更安全、合理的初始化方法。參見 Cocoa Fundamentals Guide 中Object Creation一章節。

Class Constructor Methods

類的創建對象便捷方法返回類型應該為 instancetype ,而不是 id

@interface Airplane
  (instancetype)airplaneWithType:(MyAppAirplaneType)type;
@end

參見 NSHipster.com .

CGRect Functions

獲取 CGRectxywidthheight ,應該使用 CGGeometry functions 而不是直接訪問結構體成員。 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 };

Error handling

參見 Apple's developer documentation , 可以向方法傳入NSError變量的指針獲取異常情況,調用這類方法時,需要對NSError變量進行檢查,如果錯誤需要作出合適的處理:

Preferred:

NSError *error;
BOOL success;
success = [self trySomethingWithError:amp;error];

if (!success) {
  // Handle Error
  // e.g.
  NSLog(@quot;Something bad just happened: %@quot;, error);
}

Not Preferred:

NSError *error;

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

Singletons

單例的正確創建方法:

  (instancetype)sharedInstance {
  static id sharedInstance = nil;

  // thread-safe init
  static dispatch_once_t onceToken;
  dispatch_once(amp;onceToken, ^{
    sharedInstance = [[self alloc] init];
  });

  return sharedInstance;
}

This will prevent possible and sometimes prolific crashes .

另外,還應該在dealloc中添加 abort() 方法:

- (void)dealloc {

    // implement -dealloc amp; remove abort() when refactoring for
    // non-singleton use.
    abort();
}

因為不應該給單例對象發送 dealloc 消息。參見 StackOverflow thread .

Line Breaks

避免一行太長,應該在合適的地方斷行。

Preferred:

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

Not Preferred:

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

Warnings

在target's Build Settings,中應該開啟quot;Treat Warnings as Errorsquot;,并開啟盡可能多的警告。參見 additional warnings 。如果有些警告你無法避免,可以使用 Clang's pragma feature

Log

使用 DDLog 。并使用合適的等級打印不同級別的信息。

Other Objective-C Style Guides

上面的規范可能你不太喜歡,或者沒有涉及到你需要的某些方面,可以參考下面的內容:


Tags: Objective-C

文章來源:http://www.jianshu.com/p/cb2ff45d639d


ads
ads

相關文章
ads

相關文章

ad