iOS 代码规范文档

朗境科技 移动团队 郎镜通代码规范指南

介绍

关于这个编程语言的所有规范,如果这里没有写到,那就在苹果的文档里:

目录

点语法

应该 始终 使用点语法来访问或者修改属性,访问其他实例时首选括号。

推荐:

view.backgroundColor = UIColor.orangeColor;
UIApplication.sharedApplication.delegate;

反对:

[view setBackgroundColor:[UIColor orangeColor]];
[UIApplication sharedApplication].delegate;

间距

  • 一个缩进使用 4 个空格,永远不要使用制表符(tab)缩进。请确保在 Xcode 中设置了此偏好。
  • 方法的大括号和其他的大括号(if/else/switch/while 等等)始终和声明在同一行开始,在新的一行结束。

推荐:

if (user.isHappy) {
    // Do something
}
else {
    // Do something else
}
  • 方法之间应该正好空一行,这有助于视觉清晰度和代码组织性。在方法中的功能块之间应该使用空白分开,但往往可能应该创建一个新的方法。
  • @synthesize@dynamic 在实现中每个都应该占一个新行。

条件判断

条件判断主体部分应该始终使用大括号括住来防止出错,即使它可以不用大括号(例如它只需要一行)。这些错误包括添加第二行(代码)并希望它是 if 语句的一部分时。还有另外一种更危险的,当 if 语句里面的一行被注释掉,下一行就会在不经意间成为了这个 if 语句的一部分。此外,这种风格也更符合所有其他的条件判断,因此也更容易检查。

推荐:

if (!error) {
    return success;
}

反对:

if (!error)
    return success;

if (!error) return success;

三目运算符

三目运算符,? ,只有当它可以增加代码清晰度或整洁时才使用。单一的条件都应该优先考虑使用。多条件时通常使用 if 语句会更易懂,或者重构为实例变量。

推荐:

result = a > b ? x : y;

反对:

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

错误处理

当引用一个返回错误参数(error parameter)的方法时,应该针对返回值,而非错误变量。

推荐:

NSError *error;
if (![self trySomethingWithError:&error]) {
    // 处理错误
}

反对:

NSError *error;
[self trySomethingWithError:&error];
if (error) {
    // 处理错误
}

一些苹果的 API 在成功的情况下会写一些垃圾值给错误参数(如果非空),所以针对错误变量可能会造成虚假结果(以及接下来的崩溃)。

方法

在方法签名中,在 -/+ 符号后应该有一个空格。方法片段之间也应该有一个空格。
方法名: 多个参数名前的方法要有描述性的关键字,并且不要包含and字段

推荐:

- (void)setExampleText:(NSString *)text image:(UIImage *)image;

方法命名

小写第一个单词的首字符,大写随后单词的首字符,不使用前缀。有两种例外情况:
1,方法名以广为人知的大写字母缩略词(如TIFF or PDF)开头;
2,私有方法可以使用统一的前缀来分组和辨识

表示对象行为的方法,名称以动词开头

推荐

- (void)invokeWithTarget:(id)target:
- (void)selectTabViewItem:(NSTableViewItem *)tableViewItem

方法返回接收者的某个属性,直接用属性名称命名。

推荐

- (NSSize)cellSize; 

参数前面的单词要能描述该参数

推荐

- (void)sendAction:(SEL)aSelector to:(id)anObject forAllCells:(BOOL)flag; 

方法分组

使用 #pragma mark - 方法对类的方法进行分组

#pragma mark - 声明周期
- (void)viewDidLoad {
    [super viewDidLoad];
}

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear];
}

#pragma mark - 初始化对象懒加载
- (NSMutableArray *)dataSource{
    if (!_dataSource) {
    _dataSource = [NSMutableArray array];
    }
    return _dataSource;
}

- (NSMutableArray *)tempDataArray{
    if (!_tempDataArray) {
    _tempDataArray = [NSMutableArray array];
    }
    return _tempDataArray;
}

变量

变量名应该尽可能命名为描述性的。除了 for() 循环外,其他情况都应该避免使用单字母的变量名。
星号表示指针属于变量,例如:NSString *text 不要写成 NSString* text 或者 NSString * text ,常量除外。
尽量定义属性来代替直接使用实例变量。除了初始化方法(initinitWithCoder:,等), dealloc 方法和自定义的 setters 和 getters 内部,应避免直接访问实例变量。更多有关在初始化方法和 dealloc 方法中使用访问器方法的信息,参见这里

推荐:

@interface NYTSection : NSObject

@property (nonatomic) NSString *headline;

@end

反对:

@interface NYTSection : NSObject {
    NSString *headline;
}

变量限定符

当涉及到在 ARC 中被引入变量限定符时,
限定符 (__strong, __weak, __unsafe_unretained, __autoreleasing) 应该位于星号和变量名之间,如:NSString * __weak text

命名

尽可能遵守苹果的命名约定,尤其那些涉及到内存管理规则,(NARC)的。

长的和描述性的方法名和变量名都不错,采用小驼峰命名方式。

推荐:

UIButton *settingsButton;

反对:

UIButton *setBut;

类名和常量应该始终使用字母的前缀(例如 Lenz),但 Core Data 实体名称可以省略。为了代码清晰,常量应该使用相关类的名字作为前缀并使用驼峰命名法。

推荐:

static const NSTimeInterval LenzArticleViewControllerNavigationFadeAnimationDuration = 0.3;

反对:

static const NSTimeInterval fadetime = 1.7;

属性和局部变量应该使用驼峰命名法并且首字母小写。

为了保持一致,实例变量应该使用驼峰命名法命名,并且首字母小写,以下划线为前缀。这与 LLVM 自动合成的实例变量相一致。
如果 LLVM 可以自动合成变量,那就让它自动合成。

推荐:

@synthesize descriptiveVariableName = _descriptiveVariableName;

反对:

id varnm;

注释

当需要的时候,注释应该被用来解释 为什么 特定代码做了某些事情。所使用的任何注释必须保持最新否则就删除掉。
通常应该避免一大块注释,代码就应该尽量作为自身的文档,只需要隔几行写几句说明。这并不适用于那些用来生成文档的注释。

属性注释

/** 头像image */
@property (nonatomic, strong) UIimageView *headerImage;

方法声明注释

  /**   
   * @brief 登录验证 3   
   * @param personId 用户名
   * @param password 密码 
   * @param complete 执行完毕的block 
   * @return 
   */
+ (void)loginWithPersonId:(NSString *)personId password:(NSString *)password complete:(void (^)(CheckLogon *result))complete;

宏定义

全部大写 , 单词间用_分隔 (不带参数)
推荐

#define IS_IPHONE_VERSION @"9.0"

以字母k开头,后面遵循大驼峰命名(不带参数)

#define kStatusCode @"0000000"

小驼峰命名(带参数)

#define iosDeviceVersionInfoUrl(iosinfoUrl) [NSURL URLWithString:[NSString stringWithFormat:@"%@%@",kBaseUrl,url]]

init 和 dealloc

dealloc 方法应该放在实现文件的最上面,并且刚好在 @synthesize@dynamic 语句的后面。在任何类中,init 都应该直接放在 dealloc 方法的下面。

init 方法的结构应该像这样:

- (instancetype)init {
    self = [super init]; // 或者调用指定的初始化方法
    if (self) {
        // Custom initialization
    }
    return self;
}

字面量

每当创建 NSStringNSDictionaryNSArray,和 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];

CGRect 函数

当访问一个 CGRectxywidthheight 时,应该使用CGGeometry 函数代替直接访问结构体成员。苹果的 CGGeometry 参考中说到:

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.

推荐:

CGRect frame = self.view.frame;

CGFloat x = CGRectGetMinX(frame);
CGFloat y = CGRectGetMinY(frame);
CGFloat width = CGRectGetWidth(frame);
CGFloat height = CGRectGetHeight(frame);

反对:

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;

常量

常量首选内联字符串字面量或数字,因为常量可以轻易重用并且可以快速改变而不需要查找和替换。常量应该声明为 static 常量而不是 #define ,除非非常明确地要当做宏来使用。

推荐:

static NSString * const LenzAboutViewControllerCompanyName = @"The Lenz Company";

static const CGFloat LenzImageThumbnailHeight = 50.0;

反对:

#define CompanyName @"The Lenz Company"

#define thumbnailHeight 2

枚举类型

当使用 enum 时,建议使用新的基础类型规范,因为它具有更强的类型检查和代码补全功能。现在 SDK 包含了一个宏来鼓励使用使用新的基础类型 - NS_ENUM()

推荐:

typedef NS_ENUM(NSInteger, LenzAdRequestState) {
    LenzAdRequestStateInactive,
    LenzAdRequestStateLoading
};

位掩码

当用到位掩码时,使用 NS_OPTIONS 宏。

举例:

typedef NS_OPTIONS(NSUInteger, LenzAdCategory) {
    LenzAdCategoryAutos      = 1 << 0,
    LenzAdCategoryJobs       = 1 << 1,
    LenzAdCategoryRealState  = 1 << 2,
    LenzAdCategoryTechnology = 1 << 3
};

私有属性

私有属性应该声明在类实现文件的延展(匿名的类目)中。

推荐:

@interface LenzAdvertisement ()

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

@end

图片命名

命名规则的基本思想是把文件名分成三部分,第一部分是图片的逻辑归属分类,第二部分是图片的表现内容,第三部分是图片的内容的类型,有些图片还会有第四部分,表示图片表现的状态。
1、用英文命名,不用拼音
2、每一部分用下划线分隔
3、图片名中两倍图在名字最后要加@2x,三倍图在名字最后要加@3x

推荐:

  • tab_button_search_normal@2x.png
    home_imageview_banner@2x.png
    tab_button_news_normal@3x.png

布尔

因为 nil 解析为 NO,所以没有必要在条件中与它进行比较。永远不要直接和 YES 进行比较,因为 YES 被定义为 1,而 BOOL 可以多达 8 位。

这使得整个文件有更多的一致性和更大的视觉清晰度。

推荐:

if (!someObject) {
}

反对:

if (someObject == nil) {
}

对于 BOOL 来说, 这有两种用法:

if (isAwesome)
if (![someObject boolValue])

反对:

if ([someObject boolValue] == NO)
if (isAwesome == YES) // 永远别这么做

如果一个 BOOL 属性名称是一个形容词,属性可以省略 “is” 前缀,但为 get 访问器指定一个惯用的名字,例如:

@property (assign, getter=isEditable) BOOL editable;

内容和例子来自 Cocoa 命名指南

单例

单例对象应该使用线程安全的模式创建共享的实例。

+ (instancetype)sharedInstance {
    static id sharedInstance = nil;

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

    return sharedInstance;
}

这将会预防有时可能产生的许多崩溃

导入

如果有一个以上的 import 语句,就对这些语句进行分组。每个分组的注释是可选的。
注:对于模块使用 @import 语法。

// Frameworks
@import QuartzCore;

// Models
#import "NYTUser.h"

// Views
#import "NYTButton.h"
#import "NYTUserView.h"

Xcode 工程

为了避免文件杂乱,物理文件应该保持和 Xcode 项目文件同步。Xcode 创建的任何组(group)都必须在文件系统有相应的映射。为了更清晰,代码不仅应该按照类型进行分组,也可以根据功能进行分组。

如果可以的话,尽可能一直打开 target Build Settings 中 "Treat Warnings as Errors" 以及一些额外的警告。如果你需要忽略指定的警告,使用 Clang 的编译特性

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