首页 > 代码库 > iOS开发代码规范
iOS开发代码规范
1.1类名
类名(以及类别、协议名)应首字母大写,并以驼峰格式分割单词。
1.类的前缀
1)所有类名、枚举、结构、protocol定义时最好加一个统一的标示符,可以是项目缩写,或者个人项目的名称缩写,例如都加上全大写的XM作为前缀
2)根据功能模块可以在给功能模块的类添加功能模块的名称前缀,如用户中心的profileViewController.可以命名XMProfileViewController.
1.2类的后缀
所有protocol定义时,都加上后缀Delegate 。如,XMRefreshViewDelegate,表示RefreshView的协议;
所有的控制器都加上Controller,所有的通知名都加上Notification。
类别命名
类名+标识+扩展(UIImageView +HP+Web)
//正例:
XMBankCardModel/ XMBindCardModel/ XMUUIDModel/
//反例:
xmBankCardModel / XMbindCardModel / XMUuidModel/
1.2测试类名
【强制】测试类命名以它要测试的类的名称开始,以Test结尾
//正例:
XMRequestsTest
1.3数组定义
【强制】*是数组类型的一部分,数组定义如下:
//正例:
NSArray *dataSouceArr;
1.4方法名、参数名、成员变量、局部变量
【强制】参数名、局部变量都统一使用lowerCamelCase。尽量是英文语法表达,避免使用汉语拼音来命名,严禁使用拼音与英文混合的命名方式。
//正例:
endKeyboardRect / certNoView
//反例:
EndKeyboardRect / shenFenZhengView
1.5成员变量
【强制】成员变量必须以下划线开头,统一使用lowerCamelCase。尽量是英文语法表达,避免使用汉语拼音来命名,严禁使用拼音与英文混合的命名方式。
//正例:
_selectedMonthIndex/ _isShowResult
//反例:
selectedMonthIndex/ isShowJieGuo
1.6方法名
【强制】应该在方法类型(-/+ 符号)之后有一个空格。在方法各个段之间应该也有一个空格。在参数之前应该包含一个具有描述性的关键字来描述参数,方法名统一使用lowerCamelCase。
//正例:
- (void)keyboardWasShown:(NSNotification*)notification;
//反例:
-(void)keyboardWasShown:(NSNotification*)notification; //[方法类型之后未空格]
1.7常量命名
【推荐】常量指的是宏(#define)、枚举(enum)、常量(const)等,使用小写”k“作为前缀,名称遵循大驼峰命名法
//正例:
static NSTimeInterval kAIFNetworkingTimeoutSeconds = 20.0f;
//反例:
static NSString * const codeSuccess = @"0000"
1.8 Bundle Identifier命名
【强制】采用反域名命名规则,全部使用小写字母。一级包名为com,二级包名根据应用进行命名。
//正例:
com.elong.xman
1.9设计模式命名
【推荐】如果使用到了设计模式,建议在类名中体现出具体模式。
说明:将设计模式体现在名字中,有利于阅读者快速理解架构设计思想。
//正例:
@interface AIFApiProxy : NSObject
@interface AIFServiceFactory : NSObject
1.10枚举类命名
【推荐】枚举类名和枚举成员以XM开头并使用UpperCamelCase,推荐使用新的固定基本类型规格,因为它有更强的类型检查和代码补全
说明:枚举其实就是特殊的常量类,且构造方法被默认强制是私有。
//正例:
typedef NS_ENUM(NSUInteger, XMRequestMethod) {
XMRequestMethodGET = 0,
XMRequestMethodPOST,
XMRequestMethodPUT,
XMRequestMethodDELETE,
XMRequestMethodHEAD,
};
2. 常量,字面量,枚举定义
2.1常量是容易重复被使用和无需通过查找和代替就能快速修改值。常量应该使用static来声明而不是使用#define,除非显式地使用宏。
//正例:
static NSString * const RWTAboutViewControllerCompanyName = @"RayWenderlich.com";
//反例:
#define CompanyName @"RayWenderlich.com"
2.2 NSString、NSDictionary、NSArray和NSNumber的字面值应该在创建这些类的不可变实例时被使用。请特别注意nil值不能传入NSArray和NSDictionary字面值,因为这样会导致crash。
//正例:
NSArray *names = @[@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul"];
NSDictionary *productManagers = @{@"iPhone": @"Kate", @"iPad": @"Kamal", @"Mobile Web": @"Bill"};
//反例:
NSArray *names = [NSArray arrayWithObjects:@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul", nil];
2.3当使用enum时,推荐使用新的固定基本类型规格,因为它有更强的类型检查和代码补全。现在SDK有一个宏NS_ENUM()来帮助和鼓励你使用固定的基本类型。
//正例:
typedef NS_ENUM(NSInteger, RWTLeftMenuTopItemType) {
RWTLeftMenuTopItemMain,
RWTLeftMenuTopItemShows,
RWTLeftMenuTopItemSchedule
};
//反例:
enum GlobalConstants {
kMaxPinSize = 5,
kMaxPinCount = 500,
};
3. OOP规约
3.1尽量使用不可变对象
【强制】在设计类的时候,应该充分应用属性来封装数据。具体到实践中,应该尽量把对外公布的属性设为只读(read-only),而且只在却有必要时才将属性对外公布。
3.2 通过委托和数据源协议进行对象之间的通信
【强制】对象之间通信方式有很多种,委托模式是OC中最常用的一种模式,能够将数据和业务逻辑解耦。
3.3将类的实现代码分散到便于管理的数个分类中
【强制】类中经常填满各种方法,而这些方法的代码全部堆到一个巨大的实现文件中,效果不好。可以使用OC的“分类”机制,把类代码按逻辑划入几个分区中,这样开发和调试都有好处。
3.4 在delloc方法中只释放引用并解除监听
【强制】主要释放对象所持有的引用,另外把原来配置过的观测行为都清理掉。
3.5 用弱引用避免保留环
【强制】保留环会导致内存泄漏,推荐用非拥有关系(弱引用)来解决,在ARC中,将属性声明为weak或者assign.
3.6 以自动释放池快降低内存峰值
【强制】@autoreleasepool这种方式创建,对象收到autorelease消息后,系统将其 放入最顶端的池里,可以有效降低内存的峰值。
3.7 使用dispatch_once来执行只需运行一次的线程安全的代码
【强制】通过GCD提供的 dispatch_once函数可以简化代码,并保证线程安全。
+ (DataShareManager *)sharedInstance {
static DataShareManager *sharedManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedManager = [[DataShareManager alloc] init];
});
return sharedManager;
}
3.8 提供全能的初始化方法
在类中提供一个全能的初始化方法,其他初始化方法均应该调用此方法。
3.9 实现description方法
【强制】实现description 方法返回一个有意义的字符串,用以描述该实例。
3.10使用清晰而协调的命名方式
【推荐】起名时应遵从标准的Objective-C命名规范,这样创建的接口更容易为开发者所理解。方法名要言简意赅,从左至右读起来要像个日常用语中的句子。给方法起名时第一要务就是确保风格与你自己的代码或所要集成的框架相符。
以iOS的UI库UIKit为列,演示一些命名的惯例:
1. UIView(所有视图均继承于此类),开头两个字母“UI”是UIKit框架的通用前缀。
2. UIViewController(视图类UIView)负责绘制视图
3. UITableView特殊的视图,显示表格中的一系列条目
4. UITableViewDelegate 此协议定义了表格视图与其他对象之间的通信接口,命名时,把定义“委托接口”(delegate interface)的那个类命名(UITableView)放在前面,后面加上Delegate一词,这样读起来顺口。
3.11为私有方法名加前缀
【推荐】给私有方法的名称加上前缀,这样可以很容易地将其同公共方法分开。不要单用一个下划线做私有方法的前缀,因为这种方法是预留给苹果公司用的。
3.12 在init 和 dealloc中不要用存取方法访问实例变量
【推荐】在init 和 dealloc 方法被执行时,类的运行时环境不是出于正常状态的,使用存取方法访问变量可能会导致不可预料的结果,因此应当在两个方法内直接访问实例变量。
//正例:
- (instancetype)init {
self = [super init];
if (self) {
_bar = [[NSMutableString alloc] init];
}
return self;
}
- (void)dealloc {
[_bar release];
[super dealloc];
}
3.13 保证NSString在赋值时被复制
【推荐】NSString,在它被传递或者赋值时应当保证是一复制(copy)的方式进行的,这样可以防止在不知情的情况下NSString的值被其他对象修改。
- (void)setFoo:(NSString *)aFoo {
_foo = [aFoo copy];
}
3.14理解Objective-C的错误模型
【推荐】只有发生了可使整个应用程序崩溃的严重错误时,才应使用异常。在错误不那么严重的情况下,可以指派“委托方法”来处理错误,也可以把信息错误放在NSError对象里,经由“输出参数”返回给调用者。
3.15勿在分类中声明属性
【推荐】把封装数据所用的全部属性都定义在主接口里,
在“class-continuation分类”之外的其他分类中,可以定义存取方法,但尽量不要定义属性。
4. 控制语句
4.1 switch块内通过break/return来终止
【强制】在一个switch块内,每个case要么通过break/return来终止,要么注释说明程序将继续执行到哪一个case为止;在一个switch块内,都必须包含一个default语句并且放在最后,即使它什么代码也没有。
4.2在if/for/while/switch语句中使用大括号
【强制】条件语句体应该总是被大括号包围。尽管有时候你可以不使用大括号(比如,条件语句体只有一行内容),但是这样做会带来问题隐患。比如,增加一行代码时,你可能会误以为它是 if 语句体里面的。此外,更危险的是,如果把 if 后面的那行代码注释掉,之后的一行代码会成为 if 语句里的代码。
//正例:
if (!error) {
return success;
}
4.3推荐尽量少用else
【推荐】推荐尽量少用else,如下写法:
if(condition){ ... return obj; } // 接着写else的业务逻辑代码;
说明:如果使用要if-else if-else方式表达逻辑,请勿超过3层,不要在条件语句中执行复杂的语句
4.4 Objective-C使用YES和NO
【推荐】因为true和false应该只在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.
4.5 三元操作符
【推荐】当需要提高代码的清晰性和简洁性时,三元操作符?:才会使用。单个条件求值常常需要它。多个条件求值时,如果使用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;
4.6 相等性
【推荐】当你要实现相等性的时候记住这个约定:你需要同时实现isEqual 和 hash方法。如果两个对象是被isEqual认为相等的,它们的 hash 方法需要返回一样的值。
4.7方法中需要进行参数校验的场景
【参考】方法中需要进行参数校验的场景:
1) 调用频次低的方法。
2) 执行时间开销很大的方法,参数校验时间几乎可以忽略不计,但如果因为参数错误导致中间执行回退,或者错误,那得不偿失。
3) 需要极高稳定性和可用性的方法。
4) 对外提供的开放接口,不管是DUBBO/API/HTTP接口
5. 注释
5.1 文件注释
【强制】
每一个文件都必须写文件注释,文件注释通常包含
1. 文件所在模块
2. 作者信息
3. 历史版本信息
4. 版权信息
5. 文件包含的内容,作用
一段良好文件注释的例子:
// AFNetworking.h
//
// Copyright (c) 2013 AFNetworking (http://afnetworking.com/)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
文件注释的格式通常不作要求,能清晰易读就可以了,但在整个工程中风格要统一。
5.2 属性注释
【强制】属性注释 使用 /* 注释/ 的文档注释格式。
/** 回复率*/
@property (nonatomic, strong) MTPoiCompareM *replyRate;
5.3 方法集注释
【强制】
系统有一个自带的方法集注释代码块
5.4 代码注释
【强制】方法、函数、类、协议、类别的定义都需要注释,推荐采用Apple的标准注释风格,好处是可以在引用的地方alt+点击自动弹出注释,非常方便。
有很多可以自动生成注释格式的插件,推荐使用VVDocumenter:
/**
* Get the COPY of cloud device with a given mac address.
*
* @param macAddress Mac address of the device.
*
* @return Instance of IPCCloudDevice.
*/
-(IPCCloudDevice *)getCloudDeviceWithMac:(NSString *)macAddress;
5.5 协议、委托的注释要明确说明其被触发的条件
/** Delegate - Sent when failed to init connection, like p2p failed. */
- (void)initConnectionDidFailed:(IPCConnectHandler *)handler;
6. 其它
6.1 不要使用new方法
【强制】尽管很多时候能用new代替alloc init方法,但这可能会导致调试内存时出现不可预料的问题。Cocoa的规范就是使用alloc init方法,使用new会让一些读者困惑。
6.2 #import 和 #include
【强制】
当引用的是一个Objective-C或者Objective-C++的头文件时,使用#import
当引用的是一个C或者C++的头文件时,使用#include,这时必须要保证被引用的文件提供了保护域(#define guard)
6.3 属性的线程安全
【强制】 定义一个属性时,编译器会自动生成线程安全的存取方法(Atomic),但这样会大大降低性能,特别是对于那些需要频繁存取的属性来说,是极大的浪费。所以如果定义的属性不需要线程保护,记得手动添加属性关键字nonatomic来取消编译器的优化。
6.4构建缓存时选用NSCache而非NSDictionary
【强制】NSCache可以提供优雅的自动删减功能,同时将NSPurgeableData 与 NSCache搭配使用,可实现自动清除数据的功能。
6.5 精简initialize与load的实现代码
【推荐】尽量减少在load方法中执行操作。不要在里面等待锁,也不要调用可能会加锁的方法。无法再编译器设定的全局常量,可以放在initialize方法里初始化。
iOS开发代码规范