iOS 代码规范指南

Coding

我们尽可能遵守 Apple 的命名约定, 其推荐使用长的,描述性强的方法和变量名,使其阅读起来更加清晰易懂。不能随意使用缩写,导致其他人员阅读代码困难。

驼峰命名

针对属性,变量,方法等均采用小写字母开头的驼峰命名准则。

前缀

项目名称,类名,文件名都应该保持一致的前缀名,根据 Apple Guide 建议类名前缀应该使用 2 个英文以上最好,因为 Apple 写的框架都是直接使用 2 个英文字母开头, 使用 3 个字母 能有效防止类名重复影响工程。

方法命名

根据 Cocoa 命名方法规则,我们应该准守这几个点。
1.使用小写字母开头,后面嵌套连接的字母使用大写开头。不过在写Category Method的时候, 我们比较习惯使用 JSD_method 的方式, 而非遵守所有的方法命名规则 jsd_method。 这个我觉得只要统一起来就好了。
2.对于采取动作行为的方法,使用动词开头,但是不要直接使用 do或者does
3.每个方法参数前必须带有相同或者能清晰表达其原意的关键字。
4.子类化创建相对父类更加详细功能的方法时应该把新增参数添加在原有方法的后面。
5.假如方法名过长的时候可以采用每个参数独占一行的规则,并保持每个参数分号 : 对齐的方式排列。
6.实例方法和类方法 (-/+) 符号后面应该保持一个空格, 如: - (void)。

命名属性和实例变量

1.一般属性一致采用 @property 进行声明, 特殊的数据类型可以使用实例变量声明。
2.@property (nonatomic, copy) NSString *name;属性关键字首个应该是 原子性,到内存管理关键词。如果需要写读写关键字的话, 其排在第二位.如: @property (nonatomic, readwrite, copy) NSString *name;
3.声明实例变量时必须采用 _ 下划线作为变量名前缀。
4.添加 @property 相应代码段, 提高编码速度。

命名头文件

1.头文件名必须采用一致的前缀开头, 加上其代表相关类名代表的形容词即可。尽量不要使用缩写,宁愿名字稍微长点儿,也要让其有一目了然的效果。
2.声明相关的类扩展和协议时,必须将其声明放在主类文件夹里面,这样阅读起来相对方便。
3.如果有多个功能相似的类,可以考虑将其划分为一个框架,使用 .h 文件进行声明管理。

命名 Category 方法

在写添加类别方法时, 必须采用前缀名_ 后连接对应方法名的方式来进行添加。这样就有效的避免,覆盖掉系统原有或新增方法, 导致意想不到的问题发生。
如: - (void)JS_methodName - (void)JSD_methodName

统一的ViewController 模板

针对大多数没有特别复杂逻辑的控制器实现文件, 我们制定了一套 @implementation 实现模板.
如下:

#import "JSDInvestViewController.h"

@interface JSDInvestViewController ()

@end

@implementation JSDInvestViewController

#pragma mark - 1.Life Cycle

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    //1.设置导航栏
    [self setupNavBar];
    //2.设置view
    [self setupView];
    //3.请求数据
    [self setupData];
    //4.设置通知
    [self setupNotification];
}

#pragma mark - 2.Setting View and Style

- (void)setupNavBar {
     
}

- (void)setupView {

}

#pragma mark - 3.Request Data

- (void)setupData {
   
}

可以到 Xcode 修改 ViewController 文件模板, 创建的时候即自动生成模板样式。

#pragma mark - UITableViewDataSource and UITableViewDelegate

#pragma mark - Custom Methods

#pragma mark - Set & Get

#pragma mark - Notification

#pragma mark - Event Response

点语法

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

推荐

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;
}

反对

    return success;

if (!error) return success;

三目运算符

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

推荐

####反对
Csource-objc
result = a > b ? x = c > d ? c : d : y;

在 init 和 dealloc 中不要用存取方法访问实例变量

当 init、dealloc 方法被执行时,类的运行时环境不是处于正常状态的,使用存取方法访问变量可能会导致不可预料的结果,因此应当在这两个方法内直接访问实例变量。

错误处理

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

推荐

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

反对

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

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

方法

在方法签名中,在 -/+ 符号后应该有一个空格。方法片段之间也应该有一个空格。

推荐

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

变量

变量名应该尽可能命名为描述性的。除了 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;

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

推荐

static const NSTimeInterval NYTArticleViewControllerNavigationFadeAnimationDuration = 0.3;

反对

static const NSTimeInterval fadetime = 1.7;

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

推荐

@synthesize descriptiveVariableName = _descriptiveVariableName;

反对

id varnm;

注释

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

init 和 dealloc

dealloc 方法应该放在实现文件的最下面,在任何类中,init 都应该直接放在 dealloc 方法的上面。

NSNotification

约定在我们自己定义 NSNotification 的时候应该把通知的名字定义为一个字符串常量, 就像把我们暴露给其他类的字符串常量一样。使用 extern 关键字将其在 .h 文件声明, 并且在 .m 文件对其定义。

例如:** 使用约定的 JS || JSD 作为前缀, 后跟具体通知名称, 同时推荐使用 Did/Will 这样的动词连接表示最好 **

推荐:

JSDBadgeManager.h
extern NSString *const JSDBadgeInfoDidUpdateNotification;

JSDBadgeManager.m
NSString *const JSDBadgeInfoDidUpdateNotification = @"kJSDBadgeInfoDidUpdateNotification";

Self 循环引用

我们在写 block 回调的时候, 特别是对于实例方法中调用, 很容易导致其循环引用。
我们在项目中已经引入一组强弱引用的宏定义. @weakify(self) 、@strongify(self)
我们在遇到需要修饰 Self 的时候直接使用这组宏来进行修饰即可。。

例如

[JSDStartInfo getStartInfoSuccess:^{
   @weakify(self)
   [JSDCheckVersion checkWithPass:^{
       @strongify(self);
       [self reloadView];
   }];
}];

图片管理

项目中的图片一致存放到 Assets.xcassets 进行管理. 建议后面将图片一致保存到原有的模块分组中去,然后每次不需要使用的图片,及时清除, 减少 App 体积。

** 图片名称: 图片名称可以以模块作为首接 _ 连接其相应功能点。这样在以后查找起来也相对方便 **
如: trade_result_fail 投资,结果页,状态。 discover_activity 发现,活动

TODO

当在开发某个功能中遇到一些疑难杂症的问题, 但是又由于时间紧急, 来不及把细节做到完美, 如果在不影响当前功能使用的情况下, 为了防止在后面的开发中遗忘掉, 我们必须要在相应的地方添加注释 然后以 ==TODO:== 的格式, 接相关问题描述添加进来, 待后面有空闲时间之后回来进行修复完善。

防止重复造轮子

在着手开发新需求之前,应先观察一下项目中,是否有已经封装好的相关功能类,如果已经有了,最好在当前类上添加 Catogory 进行扩展。

项目中的基础功能类, 以及对应封装的工具一致保存在 Class ---> Public。

字面量

每当创建 NSString, NSDictionary, NSArray,和 NSNumber 类的不可变实例时,都应该使用字面量。要注意 nil 值不能传给 NSArray 和 NSDictionary 字面量,这样做会导致崩溃。

推荐:

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 函数代替直接访问结构体成员。

推荐

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 NYTAboutViewControllerCompanyName = @"The New York Times Company";

static const CGFloat NYTImageThumbnailHeight = 50.0;

反对

#define CompanyName @"The New York Times Company"

#define thumbnailHeight 2

枚举类型

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

推荐


typedef NS_ENUM(NSInteger, NYTAdRequestState) {
    NYTAdRequestStateInactive,
    NYTAdRequestStateLoading
};

位掩码

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

推荐


typedef NS_OPTIONS(NSUInteger, NYTAdCategory) {
    NYTAdCategoryAutos      = 1 << 0,
    NYTAdCategoryJobs       = 1 << 1,
    NYTAdCategoryRealState  = 1 << 2,
    NYTAdCategoryTechnology = 1 << 3
};

私有属性

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

推荐


@interface NYTAdvertisement ()

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

@end

布尔

因为 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"

关于缩写

Objective-C 本身就是让阅读者读起来就像在阅读句子一样的一门语言, 变量, 常量 特别是方法名相对其他语言来说会相对长一点,主要用意就是为了表达更加清晰明了, 尽量不让阅读的人产生歧义,并且基本能达到光看代码名字就知道其用意。
所以我们也应该严格遵守语言的设计风格, 和大家保持一致, 能有效的提高我们协同开发的效率同时,也能使我们能编写出更容易让大家接受的代码。

主要可以参考文档: Cocoa guide 可接受的缩略语和首字母缩略词

Xcode 工程

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

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

你可能感兴趣的:(iOS 代码规范指南)