命名

详细介绍 Apple 的命名规约。

通用的约定

尽可能遵守 Apple 的命名约定,尤其是和 内存管理规则 (NARC) 相关的地方。推荐使用长的、描述性的方法和变量名。

  • 推荐: UIButton *settingsButton;
  • 不推荐: UIButton *setBut;

常量

常量应该以驼峰法命名,并以相关类名作为前缀。

  • 推荐: static const NSTimeInterval ZOCSignInViewControllerFadeOutAnimationDuration = 0.4;
  • 不推荐: static const NSTimeInterval fadeOutTime = 0.4;

推荐使用常量来代替字符串字面值和数字,这样能够方便复用,而且可以快速修改而不需要查找和替换。常量应该用 static 声明为静态常量,而不要用 #define,除非它明确的作为一个宏来使用。

  • 推荐:
    • static NSString * const ZOCCacheControllerDidClearCacheNotification = @"ZOCCacheControllerDidClearCacheNotification";
      static const CGFloat ZOCImageThumbnailHeight = 50.0f;
      
  • 不推荐:
    • #define CompanyName @"Apple Inc."
      #define magicNumber 42
      

常量应该在头文件中以这样的形式暴露给外部:

extern NSString *const ZOCCacheControllerDidClearCacheNotification;

并在实现文件中为它赋值。

只有公有的常量才需要添加命名空间作为前缀。尽管实现文件中私有常量的命名可以遵循另外一种模式,你仍旧可以遵循这个规则。

方法

方法名与方法类型 (-/+ 符号)之间应该以空格间隔。方法段之间也应该以空格间隔(以符合 Apple 风格)。参数前应该总是有一个描述性的关键词。

尽可能少用 "and" 这个词。它不应该用来阐明有多个参数,比如下面的 initWithWidth:height: 这个例子:

  • 推荐:
    • - (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;
      
  • 不推荐:
    • - (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.
      

字面值

使用字面值来创建不可变的 NSString, NSDictionary, NSArray, 和 NSNumber 对象。注意不要将 nil 传进 NSArrayNSDictionary 里,因为这样会导致崩溃。

  • 例子:
    • 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];
      

如果要用到这些类的可变副本,我们推荐使用 NSMutableArray, NSMutableString 这样的类。应该避免 下面这样:

NSMutableArray *aMutableArray = [@[] mutableCopy];

上面这种书写方式的效率和可读性的都存在问题。

效率方面,一个不必要的不可变对象被创建后立马被废弃了;虽然这并不会让你的 App 变慢(除非这个方法被频繁调用),但是确实没必要为了少打几个字而这样做。

可读性方面,存在两个问题:第一个问题是当你浏览代码并看见 @[] 的时候,你首先联想到的是 NSArray 实例,但是在这种情形下你需要停下来深思熟虑的检查;另一个问题是,一些新手以他的水平看到你的代码后可能会对这是一个可变对象还是一个不可变对象产生分歧。他/她可能不熟悉可变拷贝构造的含义(这并不是说这个知识不重要)。当然,不存在绝对的错误,我们只是讨论代码的可用性(包括可读性)。

类名

类名应该以三个大写字母作为前缀(双字母前缀为 Apple 的类预留)。尽管这个规范看起来有些古怪,但是这样做可以减少 Objective-C 没有命名空间所带来的问题。

一些开发者在定义模型对象时并不遵循这个规范(对于 Core Data 对象,我们更应该遵循这个规范)。我们建议在定义 Core Data 对象时严格遵循这个约定,因为最终你可能需要把你的 Managed Object Model(托管对象模型)与其他(第三方库)的 MOMs(Managed Object Model)合并。

你可能注意到了,这本书里类的前缀(不仅仅是类,也包括公开的常量、Protocol 等的前缀)是ZOC

另一个好的类的命名规范:当你创建一个子类的时候,你应该把说明性的部分放在前缀和父类名的在中间。

举个例子:如果你有一个 ZOCNetworkClient 类,子类的名字会是ZOCTwitterNetworkClient (注意 "Twitter" 在 "ZOC" 和 "NetworkClient" 之间); 按照这个约定, 一个UIViewController 的子类会是 ZOCTimelineViewController.