1 # 译者的话 2 3 4 5 代码风格的重要性对于一个团队和项目来说不言而喻。网上有许多 Objective-C 的代码风格,但这份简洁而又最符合苹果的规范,同时有助于养成良好的代码习惯,也是我们团队一直遵循的代码风格。 6 7 8 9 原文在[这里][original_link]。 10 11 本人才疏学浅,如果有任何翻译不当欢迎在 [Issues][Issues_link] 中反馈或者直接 [Fork][Fork_link] 。 12 13 14 15 [original_link]:https://github.com/NYTimes/objective-c-style-guide 16 17 18 19 [Issues_link]:https://github.com/VincentSit/NYTimes-Objective-C-Style-Guide-ZH/issues 20 21 22 23 [Fork_link]:https://github.com/NYTimes/objective-c-style-guide/fork 24 25 26 27 ---- 28 29 30 31 # 纽约时报 移动团队 Objective-C 规范指南 32 33 34 35 这份规范指南概括了纽约时报 iOS 团队的代码约定。 36 37 38 39 ## 介绍 40 41 42 43 关于这个编程语言的所有规范,如果这里没有写到,那就在苹果的文档里: 44 45 46 47 * [Objective-C 编程语言][Introduction_1] 48 49 * [Cocoa 基本原理指南][Introduction_2] 50 51 * [Cocoa 编码指南][Introduction_3] 52 53 * [iOS 应用编程指南][Introduction_4] 54 55 56 57 [Introduction_1]:http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjectiveC/Introduction/introObjectiveC.html 58 59 60 61 [Introduction_2]:https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CocoaFundamentals/Introduction/Introduction.html 62 63 64 65 [Introduction_3]:https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CodingGuidelines/CodingGuidelines.html 66 67 68 69 [Introduction_4]:http://developer.apple.com/library/ios/#documentation/iphone/conceptual/iphoneosprogrammingguide/Introduction/Introduction.html 70 71 72 73 74 75 ## 目录 76 77 78 79 * [点语法](#点语法) 80 81 * [间距](#间距) 82 83 * [条件判断](#条件判断) 84 85 * [三目运算符](#三目运算符) 86 87 * [错误处理](#错误处理) 88 89 * [方法](#方法) 90 91 * [变量](#变量) 92 93 * [命名](#命名) 94 95 * [注释](#注释) 96 97 * [Init 和 Dealloc](#init-和-dealloc) 98 99 * [字面量](#字面量) 100 101 * [CGRect 函数](#CGRect-函数) 102 103 * [常量](#常量) 104 105 * [枚举类型](#枚举类型) 106 107 * [位掩码](#位掩码) 108 109 * [私有属性](#私有属性) 110 111 * [图片命名](#图片命名) 112 113 * [布尔](#布尔) 114 115 * [单例](#单例) 116 117 * [导入](#导入) 118 119 * [Xcode 工程](#Xcode-工程) 120 121 122 123 ## 点语法 124 125 126 127 应该 **始终** 使用点语法来访问或者修改属性,访问其他实例时首选括号。 128 129 130 131 **推荐:** 132 133 ```objc 134 135 view.backgroundColor = [UIColor orangeColor]; 136 137 [UIApplication sharedApplication].delegate; 138 139 ``` 140 141 142 143 **反对:** 144 145 ```objc 146 147 [view setBackgroundColor:[UIColor orangeColor]]; 148 149 UIApplication.sharedApplication.delegate; 150 151 ``` 152 153 154 155 ## 间距 156 157 158 159 * 一个缩进使用 4 个空格,永远不要使用制表符(tab)缩进。请确保在 Xcode 中设置了此偏好。 160 161 * 方法的大括号和其他的大括号(`if`/`else`/`switch`/`while` 等等)始终和声明在同一行开始,在新的一行结束。 162 163 164 165 **推荐:** 166 167 ```objc 168 169 if (user.isHappy) { 170 171 // Do something 172 173 } 174 175 else { 176 177 // Do something else 178 179 } 180 181 ``` 182 183 * 方法之间应该正好空一行,这有助于视觉清晰度和代码组织性。在方法中的功能块之间应该使用空白分开,但往往可能应该创建一个新的方法。 184 185 * `@synthesize` 和 `@dynamic` 在实现中每个都应该占一个新行。 186 187 188 189 190 191 ## 条件判断 192 193 194 195 条件判断主体部分应该始终使用大括号括住来防止[出错][Condiationals_1],即使它可以不用大括号(例如它只需要一行)。这些错误包括添加第二行(代码)并希望它是 if 语句的一部分时。还有另外一种[更危险的][Condiationals_2],当 if 语句里面的一行被注释掉,下一行就会在不经意间成为了这个 if 语句的一部分。此外,这种风格也更符合所有其他的条件判断,因此也更容易检查。 196 197 198 199 **推荐:** 200 201 ```objc 202 203 if (!error) { 204 205 return success; 206 207 } 208 209 ``` 210 211 212 213 **反对:** 214 215 ```objc 216 217 if (!error) 218 219 return success; 220 221 ``` 222 223 224 225 或 226 227 228 229 ```objc 230 231 if (!error) return success; 232 233 ``` 234 235 236 237 238 239 [Condiationals_1]:(https://github.com/NYTimes/objective-c-style-guide/issues/26#issuecomment-22074256) 240 241 [Condiationals_2]:http://programmers.stackexchange.com/a/16530 242 243 244 245 ### 三目运算符 246 247 248 249 三目运算符,? ,只有当它可以增加代码清晰度或整洁时才使用。单一的条件都应该优先考虑使用。多条件时通常使用 if 语句会更易懂,或者重构为实例变量。 250 251 252 253 **推荐:** 254 255 ```objc 256 257 result = a > b ? x : y; 258 259 ``` 260 261 262 263 **反对:** 264 265 ```objc 266 267 result = a > b ? x = c > d ? c : d : y; 268 269 ``` 270 271 272 273 ## 错误处理 274 275 276 277 当引用一个返回错误参数(error parameter)的方法时,应该针对返回值,而非错误变量。 278 279 280 281 **推荐:** 282 283 ```objc 284 285 NSError *error; 286 287 if (![self trySomethingWithError:&error]) { 288 289 // 处理错误 290 291 } 292 293 ``` 294 295 296 297 **反对:** 298 299 ```objc 300 301 NSError *error; 302 303 [self trySomethingWithError:&error]; 304 305 if (error) { 306 307 // 处理错误 308 309 } 310 311 ``` 312 313 一些苹果的 API 在成功的情况下会写一些垃圾值给错误参数(如果非空),所以针对错误变量可能会造成虚假结果(以及接下来的崩溃)。 314 315 316 317 ## 方法 318 319 320 321 在方法签名中,在 -/+ 符号后应该有一个空格。方法片段之间也应该有一个空格。 322 323 324 325 **推荐:** 326 327 ```objc 328 329 - (void)setExampleText:(NSString *)text image:(UIImage *)image; 330 331 ``` 332 333 334 335 ## 变量 336 337 338 339 变量名应该尽可能命名为描述性的。除了 `for()` 循环外,其他情况都应该避免使用单字母的变量名。 340 341 星号表示指针属于变量,例如:`NSString *text` 不要写成 `NSString* text` 或者 `NSString * text` ,常量除外。 342 343 尽量定义属性来代替直接使用实例变量。除了初始化方法(`init`, `initWithCoder:`,等), `dealloc` 方法和自定义的 setters 和 getters 内部,应避免直接访问实例变量。更多有关在初始化方法和 dealloc 方法中使用访问器方法的信息,参见[这里][Variables_1]。 344 345 346 347 348 349 **推荐:** 350 351 352 353 ```objc 354 355 @interface NYTSection: NSObject 356 357 358 359 @property (nonatomic) NSString *headline; 360 361 362 363 @end 364 365 ``` 366 367 368 369 **反对:** 370 371 372 373 ```objc 374 375 @interface NYTSection : NSObject { 376 377 NSString *headline; 378 379 } 380 381 ``` 382 383 384 385 [Variables_1]:https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmPractical.html#//apple_ref/doc/uid/TP40004447-SW6 386 387 388 389 #### 变量限定符 390 391 392 393 当涉及到[在 ARC 中被引入][Variable_Qualifiers_1]变量限定符时, 394 395 限定符 (`__strong`, `__weak`, `__unsafe_unretained`, `__autoreleasing`) 应该位于星号和变量名之间,如:`NSString * __weak text`。 396 397 398 399 [Variable_Qualifiers_1]:(https://developer.apple.com/library/ios/releasenotes/objectivec/rn-transitioningtoarc/Introduction/Introduction.html#//apple_ref/doc/uid/TP40011226-CH1-SW4) 400 401 402 403 ## 命名 404 405 406 407 尽可能遵守苹果的命名约定,尤其那些涉及到[内存管理规则][Naming_1],([NARC][Naming_2])的。 408 409 410 411 长的和描述性的方法名和变量名都不错。 412 413 414 415 **推荐:** 416 417 418 419 ```objc 420 421 UIButton *settingsButton; 422 423 ``` 424 425 426 427 **反对:** 428 429 430 431 ```objc 432 433 UIButton *setBut; 434 435 ``` 436 437 类名和常量应该始终使用三个字母的前缀(例如 `NYT`),但 Core Data 实体名称可以省略。为了代码清晰,常量应该使用相关类的名字作为前缀并使用驼峰命名法。 438 439 440 441 **推荐:** 442 443 444 445 ```objc 446 447 static const NSTimeInterval NYTArticleViewControllerNavigationFadeAnimationDuration = 0.3; 448 449 ``` 450 451 452 453 **反对:** 454 455 456 457 ```objc 458 459 static const NSTimeInterval fadetime = 1.7; 460 461 ``` 462 463 464 465 属性和局部变量应该使用驼峰命名法并且首字母小写。 466 467 468 469 为了保持一致,实例变量应该使用驼峰命名法命名,并且首字母小写,以下划线为前缀。这与 LLVM 自动合成的实例变量相一致。 470 471 **如果 LLVM 可以自动合成变量,那就让它自动合成。** 472 473 474 475 **推荐:** 476 477 478 479 ```objc 480 481 @synthesize descriptiveVariableName = _descriptiveVariableName; 482 483 ``` 484 485 486 487 **反对:** 488 489 490 491 ```objc 492 493 id varnm; 494 495 ``` 496 497 498 499 [Naming_1]:https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/MemoryMgmt.html 500 501 502 503 [Naming_2]:http://stackoverflow.com/a/2865194/340508 504 505 506 507 ## 注释 508 509 510 511 当需要的时候,注释应该被用来解释 **为什么** 特定代码做了某些事情。所使用的任何注释必须保持最新否则就删除掉。 512 513 514 515 通常应该避免一大块注释,代码就应该尽量作为自身的文档,只需要隔几行写几句说明。这并不适用于那些用来生成文档的注释。 516 517 518 519 520 521 ## init 和 dealloc 522 523 524 525 `dealloc` 方法应该放在实现文件的最上面,并且刚好在 `@synthesize` 和 `@dynamic` 语句的后面。在任何类中,`init` 都应该直接放在 `dealloc` 方法的下面。 526 527 528 529 `init` 方法的结构应该像这样: 530 531 532 533 ```objc 534 535 - (instancetype)init { 536 537 self = [super init]; // 或者调用指定的初始化方法 538 539 if (self) { 540 541 // Custom initialization 542 543 } 544 545 546 547 return self; 548 549 } 550 551 ``` 552 553 554 555 ## 字面量 556 557 558 559 每当创建 `NSString`, `NSDictionary`, `NSArray`,和 `NSNumber` 类的不可变实例时,都应该使用字面量。要注意 `nil` 值不能传给 `NSArray` 和 `NSDictionary` 字面量,这样做会导致崩溃。 560 561 562 563 **推荐:** 564 565 566 567 ```objc 568 569 NSArray *names = @[@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul"]; 570 571 NSDictionary *productManagers = @{@"iPhone" : @"Kate", @"iPad" : @"Kamal", @"Mobile Web" : @"Bill"}; 572 573 NSNumber *shouldUseLiterals = @YES; 574 575 NSNumber *buildingZIPCode = @10018; 576 577 ``` 578 579 580 581 **反对:** 582 583 584 585 ```objc 586 587 NSArray *names = [NSArray arrayWithObjects:@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul", nil]; 588 589 NSDictionary *productManagers = [NSDictionary dictionaryWithObjectsAndKeys: @"Kate", @"iPhone", @"Kamal", @"iPad", @"Bill", @"Mobile Web", nil]; 590 591 NSNumber *shouldUseLiterals = [NSNumber numberWithBool:YES]; 592 593 NSNumber *buildingZIPCode = [NSNumber numberWithInteger:10018]; 594 595 ``` 596 597 598 599 ## CGRect 函数 600 601 602 603 当访问一个 `CGRect` 的 `x`, `y`, `width`, `height` 时,应该使用[`CGGeometry` 函数][CGRect-Functions_1]代替直接访问结构体成员。苹果的 `CGGeometry` 参考中说到: 604 605 606 607 > 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. 608 609 610 611 **推荐:** 612 613 614 615 ```objc 616 617 CGRect frame = self.view.frame; 618 619 620 621 CGFloat x = CGRectGetMinX(frame); 622 623 CGFloat y = CGRectGetMinY(frame); 624 625 CGFloat width = CGRectGetWidth(frame); 626 627 CGFloat height = CGRectGetHeight(frame); 628 629 ``` 630 631 632 633 **反对:** 634 635 636 637 ```objc 638 639 CGRect frame = self.view.frame; 640 641 642 643 CGFloat x = frame.origin.x; 644 645 CGFloat y = frame.origin.y; 646 647 CGFloat width = frame.size.width; 648 649 CGFloat height = frame.size.height; 650 651 ``` 652 653 654 655 [CGRect-Functions_1]:http://developer.apple.com/library/ios/#documentation/graphicsimaging/reference/CGGeometry/Reference/reference.html 656 657 658 659 ## 常量 660 661 662 663 常量首选内联字符串字面量或数字,因为常量可以轻易重用并且可以快速改变而不需要查找和替换。常量应该声明为 `static` 常量而不是 `#define` ,除非非常明确地要当做宏来使用。 664 665 666 667 **推荐:** 668 669 670 671 ```objc 672 673 static NSString * const NYTAboutViewControllerCompanyName = @"The New York Times Company"; 674 675 676 677 static const CGFloat NYTImageThumbnailHeight = 50.0; 678 679 ``` 680 681 682 683 **反对:** 684 685 686 687 ```objc 688 689 #define CompanyName @"The New York Times Company" 690 691 692 693 #define thumbnailHeight 2 694 695 ``` 696 697 698 699 ## 枚举类型 700 701 702 703 当使用 `enum` 时,建议使用新的基础类型规范,因为它具有更强的类型检查和代码补全功能。现在 SDK 包含了一个宏来鼓励使用使用新的基础类型 - `NS_ENUM()` 704 705 706 707 **推荐:** 708 709 710 711 ```objc 712 713 typedef NS_ENUM(NSInteger, NYTAdRequestState) { 714 715 NYTAdRequestStateInactive, 716 717 NYTAdRequestStateLoading 718 719 }; 720 721 ``` 722 723 724 725 ## 位掩码 726 727 728 729 当用到位掩码时,使用 `NS_OPTIONS` 宏。 730 731 732 733 **举例:** 734 735 736 737 ```objc 738 739 typedef NS_OPTIONS(NSUInteger, NYTAdCategory) { 740 741 NYTAdCategoryAutos = 1 << 0, 742 743 NYTAdCategoryJobs = 1 << 1, 744 745 NYTAdCategoryRealState = 1 << 2, 746 747 NYTAdCategoryTechnology = 1 << 3 748 749 }; 750 751 ``` 752 753 754 755 756 757 ## 私有属性 758 759 760 761 私有属性应该声明在类实现文件的延展(匿名的类目)中。有名字的类目(例如 `NYTPrivate` 或 `private`)永远都不应该使用,除非要扩展其他类。 762 763 764 765 **推荐:** 766 767 768 769 ```objc 770 771 @interface NYTAdvertisement () 772 773 774 775 @property (nonatomic, strong) GADBannerView *googleAdView; 776 777 @property (nonatomic, strong) ADBannerView *iAdView; 778 779 @property (nonatomic, strong) UIWebView *adXWebView; 780 781 782 783 @end 784 785 ``` 786 787 788 789 ## 图片命名 790 791 792 793 图片名称应该被统一命名以保持组织的完整。它们应该被命名为一个说明它们用途的驼峰式字符串,其次是自定义类或属性的无前缀名字(如果有的话),然后进一步说明颜色 和/或 展示位置,最后是它们的状态。 794 795 796 797 **推荐:** 798 799 800 801 * `RefreshBarButtonItem` / `[email protected]` 和 `RefreshBarButtonItemSelected` / `[email protected]` 802 803 * `ArticleNavigationBarWhite` / `[email protected]` 和 `ArticleNavigationBarBlackSelected` / `[email protected]`. 804 805 806 807 图片目录中被用于类似目的的图片应归入各自的组中。 808 809 810 811 812 813 ## 布尔 814 815 816 817 因为 `nil` 解析为 `NO`,所以没有必要在条件中与它进行比较。永远不要直接和 `YES` 进行比较,因为 `YES` 被定义为 1,而 `BOOL` 可以多达 8 位。 818 819 820 821 这使得整个文件有更多的一致性和更大的视觉清晰度。 822 823 824 825 **推荐:** 826 827 828 829 ```objc 830 831 if (!someObject) { 832 833 } 834 835 ``` 836 837 838 839 **反对:** 840 841 842 843 ```objc 844 845 if (someObject == nil) { 846 847 } 848 849 ``` 850 851 852 853 ----- 854 855 856 857 **对于 `BOOL` 来说, 这有两种用法:** 858 859 860 861 ```objc 862 863 if (isAwesome) 864 865 if (![someObject boolValue]) 866 867 ``` 868 869 870 871 **反对:** 872 873 874 875 ```objc 876 877 if ([someObject boolValue] == NO) 878 879 if (isAwesome == YES) // 永远别这么做 880 881 ``` 882 883 884 885 ----- 886 887 888 889 如果一个 `BOOL` 属性名称是一个形容词,属性可以省略 “is” 前缀,但为 get 访问器指定一个惯用的名字,例如: 890 891 892 893 ```objc 894 895 @property (assign, getter=isEditable) BOOL editable; 896 897 ``` 898 899 900 901 内容和例子来自 [Cocoa 命名指南][Booleans_1] 。 902 903 904 905 [Booleans_1]:https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CodingGuidelines/Articles/NamingIvarsAndTypes.html#//apple_ref/doc/uid/20001284-BAJGIIJE 906 907 908 909 910 911 ## 单例 912 913 914 915 单例对象应该使用线程安全的模式创建共享的实例。 916 917 918 919 ```objc 920 921 + (instancetype)sharedInstance { 922 923 static id sharedInstance = nil; 924 925 926 927 static dispatch_once_t onceToken; 928 929 dispatch_once(&onceToken, ^{ 930 931 sharedInstance = [[self alloc] init]; 932 933 }); 934 935 936 937 return sharedInstance; 938 939 } 940 941 ``` 942 943 这将会预防[有时可能产生的许多崩溃][Singletons_1]。 944 945 946 947 [Singletons_1]:http://cocoasamurai.blogspot.com/2011/04/singletons-your-doing-them-wrong.html 948 949 950 951 ## 导入 952 953 954 955 如果有一个以上的 import 语句,就对这些语句进行[分组][Import_1]。每个分组的注释是可选的。 956 957 注:对于模块使用 [@import][Import_2] 语法。 958 959 960 961 ```objc 962 963 // Frameworks 964 965 @import QuartzCore; 966 967 968 969 // Models 970 971 #import "NYTUser.h" 972 973 974 975 // Views 976 977 #import "NYTButton.h" 978 979 #import "NYTUserView.h" 980 981 ``` 982 983 984 985 986 987 [Import_1]: http://ashfurrow.com/blog/structuring-modern-objective-c 988 989 [Import_2]: http://clang.llvm.org/docs/Modules.html#using-modules 990 991 992 993 ## Xcode 工程 994 995 996 997 为了避免文件杂乱,物理文件应该保持和 Xcode 项目文件同步。Xcode 创建的任何组(group)都必须在文件系统有相应的映射。为了更清晰,代码不仅应该按照类型进行分组,也可以根据功能进行分组。 998 999 1000 1001 1002 1003 如果可以的话,尽可能一直打开 target Build Settings 中 "Treat Warnings as Errors" 以及一些[额外的警告][Xcode-project_1]。如果你需要忽略指定的警告,使用 [Clang 的编译特性][Xcode-project_2] 。 1004 1005 1006 1007 1008 1009 [Xcode-project_1]:http://boredzo.org/blog/archives/2009-11-07/warnings 1010 1011 1012 1013 [Xcode-project_2]:http://clang.llvm.org/docs/UsersManual.html#controlling-diagnostics-via-pragmas 1014 1015 1016 1017 1018 1019 # 其他 Objective-C 风格指南 1020 1021 1022 1023 如果感觉我们的不太符合你的口味,可以看看下面的风格指南: 1024 1025 1026 1027 * [Google](http://google-styleguide.googlecode.com/svn/trunk/objcguide.xml) 1028 1029 * [GitHub](https://github.com/github/objective-c-conventions) 1030 1031 * [Adium](https://trac.adium.im/wiki/CodingStyle) 1032 1033 * [Sam Soffes](https://gist.github.com/soffes/812796) 1034 1035 * [CocoaDevCentral](http://cocoadevcentral.com/articles/000082.php) 1036 1037 * [Luke Redpath](http://lukeredpath.co.uk/blog/my-objective-c-style-guide.html) 1038 1039 * [Marcus Zarra](http://www.cimgf.com/zds-code-style-guide/) 1040 1041
时间: 2024-10-24 10:25:58