├── LICENSE └── README.md /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 The New York Times Company 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 紐約時報行動軟體團隊的Objective-C程式碼撰寫風格手冊 2 | 3 | 這份手冊摘要描述紐約時報iOS開發團隊的程式碼書寫慣例。歡迎各位回饋意見,[提出問題](https://github.com/NYTimes/objetive-c-style-guide/issues)、[提出pull請求](https://github.com/NYTimes/objetive-c-style-guide/pulls)、以及[發推文](https://twitter.com/nytimesmobile),另外,我們正[招募中](http://jobs.nytco.com/job/New-York-iOS-Developer-Job-NY/2572221/)。 4 | 5 | 感謝所有的[貢獻者](https://github.com/NYTimes/objective-c-style-guide/contributors)。 6 | 7 | ## 導論 8 | 9 | 底下列出一些Apple公司關於程式碼風格與慣例的文件,若你發現某事項本手冊並未提及,通常都能在這些文件裡找到詳細的描述。 10 | 11 | * [The Objective-C Programming Language](http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjectiveC/Introduction/introObjectiveC.html) 12 | * [Cocoa Fundamentals Guide](https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CocoaFundamentals/Introduction/Introduction.html) 13 | * [Coding Guidelines for Cocoa](https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CodingGuidelines/CodingGuidelines.html) 14 | * [iOS App Programming Guide](http://developer.apple.com/library/ios/#documentation/iphone/conceptual/iphoneosprogrammingguide/Introduction/Introduction.html) 15 | 16 | ## 目錄 17 | 18 | * [句點標記法的語法(Dot-Notation Syntax)](#dot-notation-syntax) 19 | * [空格與縮排(Spacing)](#spacing) 20 | * [條件判斷(Conditionals)](#conditionals) 21 | * [方法(Methods)](#methods) 22 | * [變數(Variables)](#variables) 23 | * [命名(Naming)](#naming) 24 | * [底線(Underscores)](#underscores) 25 | * [註解(Comments)](#comments) 26 | * [初始化與解構式(init and dealloc)](#init-and-dealloc) 27 | * [字面值(Literals)](#literals) 28 | * [CGRect函式(CGRect Functions)](#cgrect-functions) 29 | * [常數(Constants)](#constants) 30 | * [列舉型別(Enumerated Types)](#enumerated-types) 31 | * [私有屬性(Private Properties)](#private-properties) 32 | * [圖檔命名(Image Naming)](#image-naming) 33 | * [布林(Booleans)](#booleans) 34 | * [單件設計模式(Singletons)](#singletons) 35 | * [Xcode專案(Xcode project)](#xcode-project) 36 | 37 | ## 句點標記法的語法(Dot-Notation Syntax) 38 | 39 | 使用句點標記法的地方一定是想要存取與改變屬性的時候,任何其他地方應採用方括號標記法。 40 | 41 | **譬如應該如下這樣寫:** 42 | ```objc 43 | view.backgroundColor = [UIColor orangeColor]; 44 | [UIApplication sharedApplication].delegate; 45 | ``` 46 | 47 | **而不是這樣寫:** 48 | ```objc 49 | [view setBackgroundColor:[UIColor orangeColor]]; 50 | UIApplication.sharedApplication.delegate; 51 | ``` 52 | 53 | ## 空格與縮排(Spacing) 54 | 55 | * 縮排時使用4個空白,千萬不要使用tab鍵,記得在Xcode的偏好設定裡啟用此選項。 56 | * 方法的大括號與其他程式構件的大括號(`if`/`else`/`switch`/`while`等等),左大括號必須跟程式構件位於同一行,但右大括號則位於新的一行。 57 | 58 | **例如:** 59 | ```objc 60 | if (user.isHappy) { 61 | //作事情 62 | } 63 | else { 64 | //作事情 65 | } 66 | ``` 67 | * 方法之間必須恰好相隔一個空白行,有助於程式碼整體組織性與視覺辨識。方法內也可用空白行區隔功能,但碰到這種情況時,通常就是該分出新方法的時候。 68 | * 在實作裡的`@synthesize`與`@dynamic`,每個必須單獨宣告佔據一行。 69 | 70 | ## 條件判斷(Conditionals) 71 | 72 | 條件判斷的主體一定要包在大括號裡,就算是能省略大括號(也就是說,主體只有一行),可避免各種 73 | [錯誤](https://github.com/NYTimes/objective-c-style-guide/issues/26#issuecomment-22074256),常見錯誤包括,之後加入第二行程式碼卻以為它會被if述句所涵蓋, 另一個會出錯且[更加危險的錯誤](http://programmers.stackexchange.com/a/16530)是,當if述句「裡」唯一一行程式碼被註解掉後,下一行卻變成if述句的主體了。另外,這項書寫慣例能與其他條件判斷式保持一致性,檢查程式碼時更容易被找出錯誤。 74 | 75 | **譬如:** 76 | ```objc 77 | if (!error) { 78 | return success; 79 | } 80 | ``` 81 | 82 | **而不是:** 83 | ```objc 84 | if (!error) 85 | return success; 86 | ``` 87 | 88 | 或 89 | 90 | ```objc 91 | if (!error) return success; 92 | ``` 93 | 94 | ## 方法(Methods) 95 | 96 | 方法宣告時,在-/+符號之後必須是一個空白。方法每個參數區段之間必須相隔一個空白。 97 | 98 | **例如:**: 99 | ```objc 100 | - (void)setExampleText:(NSString *)text image:(UIImage *)image; 101 | ``` 102 | ## 變數(Variables) 103 | 104 | 盡量以淺顯易懂的方式命名變數,避免使用只有一個字元的變數名,除了在`for()`裡。 105 | 106 | 星號指出指標屬於變數,例如`NSString *text`,而不是`NSString* text`或`NSString * text`,除非碰到常數的情況。 107 | 108 | 只要可以,都應該以屬性定義取代單純的實體變數。應避免直接存取實體變數,除了在初始化方法(`init`、`initWithCoder:`、等等)、dealloc方法、以及自訂的取值子與設值子方法。關於在初始化方法與dealloc方法裡使用存取子方法,更多細節請見[這裡](https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmPractical.html#//apple_ref/doc/uid/TP40004447-SW6)。 109 | 110 | **例如:** 111 | 112 | ```objc 113 | @interface NYTSection: NSObject 114 | 115 | @property (nonatomic) NSString *headline; 116 | 117 | @end 118 | ``` 119 | 120 | **而不是:** 121 | 122 | ```objc 123 | @interface NYTSection : NSObject { 124 | NSString *headline; 125 | } 126 | ``` 127 | 128 | ## 命名(Naming) 129 | 130 | 不論如何,都應該盡量遵守Apple的命名規則,特別是關於[記憶體管理](https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/MemoryMgmt.html) ([NARC](http://stackoverflow.com/a/2865194/340508))部份的規則。 131 | 132 | 夠長且一看就懂的方法名與變數名,是好事。 133 | 134 | **例如:** 135 | 136 | ```objc 137 | UIButton *settingsButton; 138 | ``` 139 | 140 | **而不是:** 141 | 142 | ```objc 143 | UIButton *setBut; 144 | ``` 145 | 146 | 類別名與常數應冠上由三字母組成的字頭(譬如NYT),但Core Data的entity名可省略。常數應採用駝峰式大小寫命名法,每個單字的第一個字母應大寫,為了清楚起見冠上相關的類別名。 147 | 148 | 149 | **例如:** 150 | 151 | ```objc 152 | static const NSTimeInterval NYTArticleViewControllerNavigationFadeAnimationDuration = 0.3; 153 | ``` 154 | 155 | **而不是:** 156 | 157 | ```objc 158 | static const NSTimeInterval fadetime = 1.7; 159 | ``` 160 | 161 | 屬性名也採用駝峰式大小寫命名法,但開頭單字應小寫,注意:若Xcode能自動合成實體變數,就讓它負責;否則,屬性背後的實體變數的名字,應採用駝峰式大小寫命名法但開頭單字小寫,並加上底線(_),這也是Xcode合成名字時的預設規則。 162 | 163 | **例如:** 164 | 165 | ```objc 166 | @synthesize descriptiveVariableName = _descriptiveVariableName; 167 | ``` 168 | 169 | **而不是:** 170 | 171 | ```objc 172 | id varnm; 173 | ``` 174 | 175 | ### 底線(Underscores) 176 | 177 | 使用屬性時,一定使用`self.`來存取實體變數,這麼一來,一看就能看出所有的屬性,因為其前頭都有`self.`。區域變數不該含有底線。 178 | 179 | ## 註解(Comments) 180 | 181 | 當需要寫註解時,其功用應該是試著解釋「為什麼」這一段程式碼做了某件事。任何註解都應該隨時更新,否則就該刪除。 182 | 183 | 一般來說,應避免使用區塊型註解,因為程式碼應盡量保持「一看就能明白」的樣子,只需要偶爾插入少數幾行的說明描述即可。但這項慣例不適用於用來產生文件的註解。 184 | 185 | ## 初始化與解構式(init and dealloc) 186 | 187 | `dealloc`方法應位於實作的最上方,緊跟著 `@synthesize`與`@dynamic`,而`init`方法應置於`dealloc` 之下。 188 | 189 | ## 字面值(Literals) 190 | 191 | 建立`NSString`、`NSDictionary`、`NSArray`、與`NSNumber`的不可變物件時,應使用其字面值。請特別注意, 不可將`nil`值放進`NSArray`與`NSDictionary`的字面值,將會導致當掉。 192 | 193 | **例如:** 194 | 195 | ```objc 196 | NSArray *names = @[@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul"]; 197 | NSDictionary *productManagers = @{@"iPhone" : @"Kate", @"iPad" : @"Kamal", @"Mobile Web" : @"Bill"}; 198 | NSNumber *shouldUseLiterals = @YES; 199 | NSNumber *buildingZIPCode = @10018; 200 | ``` 201 | 202 | **而不是:** 203 | 204 | ```objc 205 | NSArray *names = [NSArray arrayWithObjects:@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul", nil]; 206 | NSDictionary *productManagers = [NSDictionary dictionaryWithObjectsAndKeys: @"Kate", @"iPhone", @"Kamal", @"iPad", @"Bill", @"Mobile Web", nil]; 207 | NSNumber *shouldUseLiterals = [NSNumber numberWithBool:YES]; 208 | NSNumber *ZIPCode = [NSNumber numberWithInteger:10018]; 209 | ``` 210 | 211 | ## CGRect函式(CGRect Functions) 212 | 213 | 存取`CGRect`的`x`、`y`、`width`、或`height`時,一律使用[`CGGeometry`函式](http://developer.apple.com/library/ios/#documentation/graphicsimaging/reference/CGGeometry/Reference/reference.html),而不要直接存取結構成員。摘錄Apple的`CGGeometry`參考文件: 214 | 215 | > 本參考文件中,所有接受CGRect資料結構作為輸入的函式,都會暗中將這些矩形予以標準化,所以,您的程式碼應避免直接讀寫存放在CGRect裡的資料,反之,請使用此處列出的函式來操作矩形與各項特性。 216 | 217 | **例如:** 218 | 219 | ```objc 220 | CGRect frame = self.view.frame; 221 | 222 | CGFloat x = CGRectGetMinX(frame); 223 | CGFloat y = CGRectGetMinY(frame); 224 | CGFloat width = CGRectGetWidth(frame); 225 | CGFloat height = CGRectGetHeight(frame); 226 | ``` 227 | 228 | **而不是:** 229 | 230 | ```objc 231 | CGRect frame = self.view.frame; 232 | 233 | CGFloat x = frame.origin.x; 234 | CGFloat y = frame.origin.y; 235 | CGFloat width = frame.size.width; 236 | CGFloat height = frame.size.height; 237 | ``` 238 | 239 | ## 常數(Constants) 240 | 241 | 比起在程式碼裡直接寫入字串字面值或數字,較佳的作法是使用常數,這麼一來便能輕易地重複使用,想修改時也很方便,不需要大海撈針逐一更動。常數應宣告為`static`常數、而不是以`#define`定義,除非是作為巨集之用。 242 | 243 | **例如:** 244 | 245 | ```objc 246 | static NSString * const NYTAboutViewControllerCompanyName = @"The New York Times Company"; 247 | 248 | static const CGFloat NYTImageThumbnailHeight = 50.0; 249 | ``` 250 | 251 | **而不是:** 252 | 253 | ```objc 254 | #define CompanyName @"The New York Times Company" 255 | 256 | #define thumbnailHeight 2 257 | ``` 258 | 259 | ## 列舉型別(Enumerated Types) 260 | 261 | 使用`enum`時,建議使用擁有底層固定型別的新型宣告,因為可得到更佳的型別檢查與程式碼補齊功能;SDK裡含有巨集`NS_ENUM()`,方便我們加以運用。 262 | 263 | **例如:** 264 | 265 | ```objc 266 | typedef NS_ENUM(NSInteger, NYTAdRequestState) { 267 | NYTAdRequestStateInactive, 268 | NYTAdRequestStateLoading 269 | }; 270 | ``` 271 | 272 | ## 私有屬性(Private Properties) 273 | 274 | 私有屬性應宣告在類別實作檔裡的類別延伸(class extension)裡,也就是無名類目(anonymous category),不該使用有名的類目(例如`NYTPrivate`或`private`),除非你想要延伸擴充某類別。 275 | 276 | **例如:** 277 | 278 | ```objc 279 | @interface NYTAdvertisement () 280 | 281 | @property (nonatomic, strong) GADBannerView *googleAdView; 282 | @property (nonatomic, strong) ADBannerView *iAdView; 283 | @property (nonatomic, strong) UIWebView *adXWebView; 284 | 285 | @end 286 | ``` 287 | 288 | ## 圖檔命名(Image Naming) 289 | 290 | 請務必讓圖檔名保持一致性,便於管理,才不會讓開發人員發瘋,應採用駝峰式大小寫命名法,首先詳細地描述出該圖檔的用途,然後跟著與它們相關、不帶有前置字串的類別名或屬性名(若有的話),然後跟著顏色、編排等資訊,最後是圖檔的狀態。 291 | 292 | **例如:** 293 | 294 | * `RefreshBarButtonItem` / `RefreshBarButtonItem@2x` 和 `RefreshBarButtonItemSelected` / `RefreshBarButtonItemSelected@2x` 295 | * `ArticleNavigationBarWhite` / `ArticleNavigationBarWhite@2x` 和 `ArticleNavigationBarBlackSelected` / `ArticleNavigationBarBlackSelected@2x`. 296 | 297 | 用途類似的圖檔,應置於Images目錄下,分門別類放在相對應的群組之下。 298 | 299 | ## 布林(Booleans) 300 | 301 | 因為`nil`會被決議成`NO`,所以在條件判斷時並必須要與之作比較。千萬不要將某物直接與`YES`作比較,因為`YES`被定義為1,而`BOOL`可能是8位元的任何值。 302 | 303 | 遵守此規則的話,檔案間便能擁有更高的一致性,閱讀程式碼時也較清楚明白。 304 | 305 | **例如:** 306 | 307 | ```objc 308 | if (!someObject) { 309 | } 310 | ``` 311 | 312 | **而不是:** 313 | 314 | ```objc 315 | if (someObject == nil) { 316 | } 317 | ``` 318 | 319 | ----- 320 | 321 | **若是BOOL型別,底下有兩個例子:** 322 | 323 | ```objc 324 | if (isAwesome) 325 | if (![someObject boolValue]) 326 | ``` 327 | 328 | **而不是:** 329 | 330 | ```objc 331 | if ([someObject boolValue] == NO) 332 | if (isAwesome == YES) // 千萬別這麼寫 333 | ``` 334 | 335 | ----- 336 | 337 | 若BOOL屬性的名字是個形容詞,那麼可省略“is”前置字串,但可為取值子方法設定約定俗成的名稱,例如: 338 | 339 | ```objc 340 | @property (assign, getter=isEditable) BOOL editable; 341 | ``` 342 | 描述與範例取自[Cocoa Naming Guidelines](https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CodingGuidelines/Articles/NamingIvarsAndTypes.html#//apple_ref/doc/uid/20001284-BAJGIIJE)。 343 | 344 | ## 單件設計模式(Singletons) 345 | 346 | 建立共享的單件物件時,應使用執行緒安全的寫法。 347 | Singleton objects should use a thread-safe pattern for creating their shared instance. 348 | ```objc 349 | + (instancetype)sharedInstance { 350 | static id sharedInstance = nil; 351 | 352 | static dispatch_once_t onceToken; 353 | dispatch_once(&onceToken, ^{ 354 | sharedInstance = [[self alloc] init]; 355 | }); 356 | 357 | return sharedInstance; 358 | } 359 | ``` 360 | 這麼做可免除[各種可能出現的臭蟲與錯誤] (http://cocoasamurai.blogspot.com/2011/04/singletons-your-doing-them-wrong.html)。 361 | 362 | ## Xcode專案(Xcode project) 363 | 364 | 實體檔案應隨時與Xcode專案檔保持同步,避免檔案四處流散;Xcode專案裡的群組(group)必須在檔案系統中有其相對應的目錄;不僅要根據類型區分程式碼,也要根據功能加以劃分,使之更為清晰。 365 | 366 | 盡量啟用建構標的之Treat Warnings as Errors這項建構設定,並盡量啟用[額外的警告](http://boredzo.org/blog/archives/2009-11-07/warnings),越多越好,若你需要忽略某一項特定的警告,可使用[Clang的pragma功能](http://clang.llvm.org/docs/UsersManual.html#controlling-diagnostics-via-pragmas)。 367 | 368 | # 其他關於Objective-C程式碼撰寫風格的手冊 369 | 370 | 如果我們的規範並不符合您的口味,請看看其他人的風格手冊: 371 | 372 | * [Google](http://google-styleguide.googlecode.com/svn/trunk/objcguide.xml) 373 | * [GitHub](https://github.com/github/objective-c-conventions) 374 | * [Adium](https://trac.adium.im/wiki/CodingStyle) 375 | * [Sam Soffes](https://gist.github.com/soffes/812796) 376 | * [CocoaDevCentral](http://cocoadevcentral.com/articles/000082.php) 377 | * [Luke Redpath](http://lukeredpath.co.uk/blog/my-objective-c-style-guide.html) 378 | * [Marcus Zarra](http://www.cimgf.com/zds-code-style-guide/) 379 | --------------------------------------------------------------------------------