iOS组代码规范

不规范的代码,读起来身心疲惫,改起来后患无穷

iOS成员编写代码我们应该遵循以下规范:


1. 语言

  • 我们应该正确地使用英语

首选
UIColor *myColor = [UIColor whiteColor];
不可选
UIColor *myColour = [UIColor whiteColor]; 

2. 代码组织

  • 使用#pragma mark - 分类方法在功能分组和协议/委托实现这一总体结构。

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

3. 间距

  • 使用2空格缩进,不要用tabs进行缩进,一定要在Xcodepreference中设置

  • 方法括号和其他括号(if/else/switch/while 等),开始括号一定要在相同行展开而且关闭括号在新行关闭

首选
if (user.isHappy) {
  //Do something
} else {
  //Do something else
}
不可选
if (user.isHappy)
{
    //Do something
}
else {
    //Do something else
}

4. 注释

  • 当某些代码需要的时候,就需要有注释去说明这些代码做了什么功能,但当代码有更新的时候注释也需要及时去更新

  • 避免一整块的注释,因为代码应该是保持可读性比较高,而只需要一段精简的注释,但是这些注释并不适用于文档的编写

5. 如何命名

  • 苹果官方的命名规范我们应该时刻遵守,特别是那些涉及到内存管理方面的规则(NARC)

  • 方法名和变量名应该精准地描述其含义

首选
UIButton *settingsButton;
不可选
UIButton *setBut;
  • 常量应该“骆峰式”单词大写和相关的类名称前缀的清晰度。

首选
static NSTimeInterval const RWTTutorialViewControllerNavigationFadeAnimationDuration = 0.3;
不可选
static NSTimeInterval const fadetime = 1.7;
  • 属性应该驼峰-大小写。使用属性而不是手动auto-synthesis @ synthesize语句,除非你有充分的理由。

首选
@property (strong, nonatomic) NSString *descriptiveVariableName;
不可选
id varnm;

6. 强调

  • 在使用属性时,应该始终使用self访问和修改实例变量,这意味着所有属性在视觉上都是不同的,因为它们都以self开头。

  • 这里有一个例外:在初始化器内部,应该直接使用 _ 来调用实例变量(即_variableName),以避免getter /setter的任何潜在副作用。局部变量不应该包含下划线。

7. 方法

  • 在方法实现中,方法类型(-/+符号)后面应该有一个空格。方法段之前应该有一个空格(跟苹果官方风格保持一致)。必须使用包含一个关键字描述该方法段在该方法里面所起到的作用。

  • 单词“and”的用法有所保留,他不应该用于多个参数,如initWithWidth:height: example

首选
- (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.

8. 变量

  • 变量应尽可能用描述性的方式命名,除了for()循环,应该避免使用单字母变量名
  • 应该正确规范地命名变量,例如NSString *text,而不是NSString* text或者NSString * text,除非是定义常量
  • 应该尽可能使用私有属性来替代实例变量,虽然实例变量是一种有效的命名方式。但是团队通过统一使用属性,则代码风格更加一致

  • 除了初始化方法(init、initWithCoder)、dealloc方法和自定义settergetter方法中,应该避免直接访问“back”属性的实例变量。有关初始化方法的更多信息,请参考这里
首选
@interface RWTTutorial : NSObject

@property (strong, nonatomic) NSString *tutorialName;

@end
不可选
@interface RWTTutorial : NSObject {
  NSString *tutorialName;
}

9. Property属性

  • Property属性应该明确地列出来,这将有助于新程序员阅读代码。属性地顺序应该是存储他们地原子性,这与从Interface Builder连接UI元素时自动生成地代码保持一致
首选
@property (weak, nonatomic) IBOutlet UIView *containerView;
@property (strong, nonatomic) NSString *tutorialName;
不可选
@property (nonatomic, weak) IBOutlet UIView *containerView;
@property (nonatomic) NSString *tutorialName;
  • 对具有可变地属性(例如NSString)应该更倾向于copy而不是strong。为什么?即使你声明了一个属性NSString,有人有可能会传入一个NSMutableString地实例,然后在你没有注意到地状况下改变它
首选
@property (copy, nonatomic) NSString *tutorialName;
不可选
@property (strong, nonatomic) NSString *tutorialName;

10. 点 语法的调用

  • 点 语法纯粹是一个方便的访问器方法调用包装器。在使用点语法时,仍然调用了getter/setter方法访问或更改属性,请参考这里
  • 点符号应该用于访问和修改属性,因为它使代码更加简洁。在所有其他的实例中,括号表示法是首选的

首选
NSInteger arrayCount = [self.array count];
view.backgroundColor = [UIColor orangeColor];
[UIApplication sharedApplication].delegate;
不可选
NSInteger arrayCount = self.array.count;
[view setBackgroundColor:[UIColor orangeColor]];
UIApplication.sharedApplication.delegate;

11.不可变实例的赋值

  • 在创建这些不可变实例时,应该使用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 *buildingStreetNumber = @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 *buildingStreetNumber = [NSNumber numberWithInteger:10018];

12.常量

  • 常量比代码内字符串或者其他的表达方式更受欢迎,因为他们可以方便地复制常用变量,而且不需要查找和替换就可以快速更改。常量应该声明为静态变量,而不是用#定义,除非显示地用作宏
首选
static NSString * const DeliCompanyAddress = @"www.delicloud.com";

static CGFloat const RWTImageThumbnailHeight = 50.0;
不可选
#define DeliCompanyAddress @"www.delicloud.com"

#define thumbnailHeight 2

13.枚举类型

  • 在使用枚举时,建议使用新的固定的底层类型规范,因为它具有更强的类型检查和代码完成能力。Apple SDK提供了包含一个宏来促进和鼓励使用固定的底层类型:NS_ENUM()
举个例子:
typedef NS_ENUM(NSInteger, RWTLeftMenuTopItemType) {
  RWTLeftMenuTopItemMain,
  RWTLeftMenuTopItemShows,
  RWTLeftMenuTopItemSchedule
};
  • 您还可以进行显示值赋值(比较老的k-风格常量定义)
typedef NS_ENUM(NSInteger, RWTGlobalConstants) {
  RWTPinSizeMin = 1,
  RWTPinSizeMax = 5,
  RWTPinCountMin = 100,
  RWTPinCountMax = 500,
};
  • 应该避免使用旧的k-风格常量定义,除非编写CoreFoundation C代码(一般不会用到)
不可选
enum GlobalConstants {
  kMaxPinSize = 5,
  kMaxPinCount = 500,
};

14.各种类型的判断(Switch

  • 括号在类型判断中不是必须的,除非编译器强制需要。当我们编写代码超过单行时,此时括号应该需要加上
switch (condition) {
  case 1:
    // ...
    break;
  case 2: {
    // ...
    // 多行需要用到括号
    break;
  }
  case 3:
    // ...
    break;
  default: 
    // ...
    break;
}
  • 有时相同的代码可以用于多种情况,并且应该需要一个默认情况。如果情况相同则移除break,从而允许执行流传递给下一个case,为了代码的清晰性,应该注释 移除break的部分
switch (condition) {
  case 1:
    // ** 注释移除`break`的原因 **
  case 2:
    // code executed for values 1 and 2
    break;
  default: 
    // ...
    break;
}
  • 当为switch使用枚举类型时,不需要default。例如:
RWTLeftMenuTopItemType menuType = RWTLeftMenuTopItemMain;

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

15. 私有属性

  • 私有属性应该在类的实现文件中的类扩展(匿名类别)中声明。命名类别(如RWTPrivate或private)除非扩展另一个类,否则永远不应该使用。可以共享/公开匿名类别,以便使用+Private进行测试
举个例子:
@interface RWTDetailViewController ()

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

@end

16. Booleans

  • Objective-C使用YESNO。因此,truefalse只能用于CoreFoundation、C或c++代码。因为nil解为NO,所以没有必要在条件下进行比较。永远不要直接比较YES,因为YES的定义是1,BOOL最多可以是8位。
  • 通过这种统一,可以使文件之间有更强的一致性和更大的视觉清晰度
首选
if (someObject) {}
if (![anotherObject boolValue]) {}
不可选
if (someObject == nil) {}
if ([anotherObject boolValue] == NO) {}
if (isAwesome == YES) {} // Never do this.
if (isAwesome == true) {} // Never do this.
  • 如果BOOL属性的名称表示为形容词,则该属性可以省略“is”前缀,但指定get访问器的常规名称,例如:
@property (assign, getter=isEditable) BOOL editable;

17. 判断条件if

  • 判断条件应该始终使用大括号,即使在没有大括号(例如只有一行代码)的情况下,也应该使用大括号来防止错误。这些错误包括添加第二行并期望它成为if-statement的一部分。另一个更危险的缺陷可能发生在if-statement中的“内部”行被注释掉,而下一行不自觉地成为if-statement的一部分。此外,这种样式更符合所有其他条件,因此更容易扫描
首选
if (!error) {
  return success;
}
不可选
if (!error)
  return success;
if (!error) return success;

18. 三元运算符

  • 三元运算符,?:,它的作用为增加清晰性或者使代码整洁。一个条件通常是所有情况都需要评估的。评估多个条件通常使用if语句或者重构为实例变量更容易理解。通常,三元运算符的最佳用法是在分配变量和决定使用哪个值的过程中进行使用
  • 将非布尔变量与其他变量进行比较时,并添加括号以提高可读性。如果要比较的变量是布尔类型,则不需要括号
首选
NSInteger value = 5;
result = (value != 0) ? x : y;

BOOL isHorizontal = YES;
result = isHorizontal ? x : y;
不可选
result = a > b ? x = c > d ? c : d : y;

19. 初始化方法

  • init方法应该遵循Apple生成的代码模版提供的约定。还应该使用instancetype的返回类型而不是id
模版
- (instancetype)init {
  self = [super init];
  if (self) {
    // ...
  }
  return self;
}
  • 有关instancetype文章的链接,请参阅下方:类构造函数方法

20. 类的构造函数方法

  • 在使用类构造函数方法时,这些方法应该总是返回instancetype的类型,而不是id。这确保编译器正确地推断结果类型
举个例子:
@interface Airplane
+ (instancetype)airplaneWithType:(RWTAirplaneType)type;
@end
  • 关于instancetype的更多信息可以在NSHipster.com上找到

21. CGRect功能

  • 当访问CGRectx、y,宽度或高度时,总是使用CGGeometry函数而不是直接访问struct成员,来自苹果的CGGeometry参考:

在计算矩形的结果之前,将CGRect数据结构作为输入的所有函数都隐式地标准化了这些矩形。因此,应用程序应该避免直接读写存储在CGRect数据结构重的数据。相反,使用这里描述的函数来操作矩形并检索他们的特征
首选
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);
不可选
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 };

22. 黄金路线

  • 在有条件的情况下编码时,代码应该是“黄金”或“快乐”路径。也就是说,不要嵌套if语句,多个返回(return)语句都可以
首选
- (void)someMethod {
  if (![someOther boolValue]) {
    return;
  }

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

23. 黄金路线

  • 当方法通过引用错误参数时,请打开返回值,而不是错误变量
首选
NSError *error;
if (![self trySomethingWithError:&error]) {
  // Handle Error
}
不可选
NSError *error;
[self trySomethingWithError:&error];
if (error) {
  // Handle Error
}
  • 在成功的情况下,Apple的一些api会将垃圾值写入错误参数(如果非null),因此,打开错误可能会导致判断否定(然后导致崩溃)

24. 单例

  • 单例对象应该使用线程安全模式来创建它们的共享实例
+ (instancetype)sharedInstance {
  static id sharedInstance = nil;

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

  return sharedInstance;
}
  • 会防止有可能的、有时大量的崩溃

25. 换行符

  • 换行符是一个重要的主题,因为这个样式决定打印log的情况以及代码的可读性控制
例如
self.productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:productIdentifiers];
  • 像这样的一行长代码应该按照这个样式空格分开继续写到第二行
self.productsRequest = [[SKProductsRequest alloc] 
  initWithProductIdentifiers:productIdentifiers];

26. 换行符

  • 笑脸是raywenderlich.com网站一个非常突出的风格特征!有一个正确的微笑是非常重要的,它意味着巨大的快乐和兴奋的编码主题。使用方括号是因为它代表了能够使用ascii艺术捕获的最大的微笑。如果使用了结束括号,则表示半心半意的微笑,因此不受欢迎
首选
:]
不可选
:)

27. Xcode工程

  • 物理文件应该与Xcode项目文件保持同步,以避免文件混乱。创建Xcode组都应该由文件系统中的文件夹反映。代码不仅应该按类型分组,还应该按特性分组,以获得更清晰的内容
  • 在可能的情况下,打开目标构建设置中的“将警告视为错误”,并启用尽可能多的附加警告。如果需要忽略特定的警告,请使用Clang的pragma特性

其他Objective-C风格指南

谷歌代码规范

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,657评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,662评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,143评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,732评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,837评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,036评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,126评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,868评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,315评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,641评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,773评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,859评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,584评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,676评论 2 351

推荐阅读更多精彩内容

  • 这是16年5月份编辑的一份比较杂乱适合自己观看的学习记录文档,今天18年5月份再次想写文章,发现简书还为我保存起的...
    Jenaral阅读 2,739评论 2 9
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,637评论 18 139
  • 销售首先要了解自己的产品,专业的知识能让你精准识别客户。这时候再考虑售卖的技巧。 新人入职要想快速熟悉陌生领...
    零1阅读 155评论 0 0
  • 最新版本[1.0] ' 下载地址[http://hao.360.cn/?src=lm&ls=n5e9a0f4...
    春风_ce41阅读 103评论 0 0
  • 小程序爬坑日记 最近因公司安排首次接触了微信小程序的开发,本来仗着有Vue的基础以为很简单,没想到开发过程中还是遇...
    棒棒的程序员阅读 1,694评论 2 10