iOS 基础编码规范
目录
- 代码格式化
- 空格
- 代码组织
- Pragma
- 命名
- 通用的约定
- 常量
- 方法
- 字面量
- 类
- 类名
- 初始化
- 属性
- 属性变量
- 点符号
- 属性定义
- 私有属性
- 懒加载
- 条件语句
- if else
- 三目运算符
- Case语句
- 枚举类型
- 分类
代码格式化
- 空格
缩进使用4个空格,不要使用 tab, 确保你在 Xcode 的设置里面是这样设置的
方法的大括号和其他的大括号(if/else/switch/while 等) 总是在同一行开始,在新起一行结束
推荐:
if (user.isHappy) {
//Do something
}
else {
//Do something else
}
不推荐:
if (user.isHappy)
{
//Do something
} else {
//Do something else
}
应该总是让冒号对齐。有一些方法签名可能超过三个冒号,用冒号对齐可以让代码更具有可读性。即使有代码块存在,也应该用冒号对齐方法
推荐:
[UIView animateWithDuration:1.0
animations:^{
// something
}
completion:^(BOOL finished) {
// something
}];
不推荐:
[UIView animateWithDuration:1.0 animations:^{
// something
} completion:^(BOOL finished) {
// something
}];
代码组织
Pragma Mark -
#pragma mark -
是一个在类内部组织代码并且帮助你分组方法,我们建议使用 #pragma mark - 来做方法分组
#pragma mark - lifeCycle (View 的生命周期)
- (void)viewDidLoad { /* ... */ }
- (void)viewWillAppear:(BOOL)animated { /* ... */ }
- (void)didReceiveMemoryWarning { /* ... */ }
#pragma mark - private methods (私有函数)
- (void)setCustomProperty:(id)value { /* ... */ }
- (id)customProperty { /* ... */ }
#pragma mark - event response (响应事件)
- (IBAction)submitData:(id)sender { /* ... */ }
#pragma mark - public methods (公开函数)
- (void)publicMethod { /* ... */ }
#pragma mark - UITableViewDataSource
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { /* ... */ }
#pragma mark - UITableViewDelegate
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView;
#pragma mark - getter\setter
- (NSArray *)name {
if (!_name) {
_name = [[NSArray alloc] init];
}
return _name;
}
- (void)setName:(NSArray *)name {
_name = name;
}
命名
- 通用的约定
推荐使用具有清晰描述性的方法名或变量名
推荐:
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;
并在实现文件中为它赋值
NSString * ZOCCacheControllerDidClearCacheNotification = @"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 传进 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];
如果要用到这些类的可变副本
推荐:
NSMutableArray *aMutableArray = [NSMutableArray array];
不推荐:
NSMutableArray *aMutableArray = [@[] mutableCopy];
类
- 类名
每一个类文件名称,我们按照规范加上前缀,默认是以公司缩写作为前缀: WLxxx
- 初始化
推荐:
- (instancetype)init {
self = [super init]; // call the designated initializer
if (self) {
// Custom initialization
}
return self;
}
不推荐:
- (instancetype)init {
if ([super init]) {
// Custom initialization
}
return self;
}
- 属性
属性变量
推荐:
NSString *text;
不推荐:
NSString* text;
NSString * text;
点符号
当使用 setter getter 方法的时候尽量使用点符号。应该总是用点符号来访问以及设置属性。
推荐:
view.backgroundColor = [UIColor orangeColor];
[UIApplication sharedApplication].delegate;
不推荐:
[view setBackgroundColor:[UIColor orangeColor]];
UIApplication.sharedApplication.delegate;
属性定义
推荐按照下面的格式来定义属性
推荐:
@property (nonatomic, readwrite, copy) NSString *name;
不推荐:
@property (nonatomic, copy, readwrite) NSString *name;
属性的参数应该按照下面的顺序排列:原子性,读写 和 内存管理。这样做你的属性更容易修改正确,并且更好阅读。(译者注:习惯上修改某个属性的修饰符时,一般从属性名从右向左搜索需要修动的修饰符。最可能从最右边开始修改这些属性的修饰符,根据经验这些修饰符被修改的可能性从高到底应为:内存管理 > 读写权限 >原子操作)
为了完成一个公有的 getter 和一个私有的 setter,应该声明公开的属性为 readonly,并且在类扩展中重新定义通用的属性为 readwrite
//.h文件中
@interface MyClass : NSObject
@property (nonatomic, readonly, strong) NSObject *object;
@end
//.m文件中
@interface MyClass ()
@property (nonatomic, readwrite, strong) NSObject *object;
@end
@implementation MyClass
//Do Something cool
@end
描述BOOL属性的词如果是形容词,那么setter不应该带is前缀,但它对应的 getter 访问器应该带上这个前缀
示例:
@property (assign, getter=isEditable) BOOL editable;
私有属性
私有属性应该定义在类的实现文件中,不应该定义在申明文件中
.m文件
@interface ZOCViewController ()
@property (nonatomic, strong) UIView *bannerView;
@end
懒加载
实例化一个对象需要耗费很多资源,或者配置一次就要调用很多配置相关的方法而你又不想弄乱这些方法时,我们需要重写 getter 方法以延迟实例化,而不是在 init 方法里给对象分配内存
推荐:
- (NSArray *)nameArray {
if (!_nameArray) {
_nameArray = [[NSArray alloc] init];
}
return _nameArray;
}
条件语句
条件语句体应该总是被大括号包围,尽管有时候你可以不使用大括号(比如,条件语句体只有一行内容),但是这样做会带来问题隐患。比如,增加一行代码时,你可能会误以为它是 if 语句体里面的。此外,更危险的是,如果把 if 后面的那行代码注释掉,之后的一行代码会成为 if 语句里的代码
- if
推荐:
if (!error) {
return success;
}
不推荐:
if (!error)
return success;
if (!error) return success;
- 三目运算符
推荐:
result = a > b ? x : y;
不推荐:
result = a > b ? x = c > d ? c : d : y;
Case语句
括号在 case 语句里面是不必要的,但是当一个 case 包含了多行语句的时候,需要加上括号
示例:
switch (condition) {
case 1:
// ...
break;
case 2: {
// ...
// Multi-line example using braces
break;
}
case 3:
// ...
break;
default:
// ...
break;
}
当两个条件语句执行的是同一个代码块时,我们可以合并条件语句
示例:
switch (condition) {
case 1:
case 2:
// code executed for values 1 and 2
break;
default:
// ...
break;
}
当在 switch 语句里面使用一个可枚举的变量作为判断条件时,default我们可以省略掉
示例:
switch (menuType) {
case ZOCEnumNone:
// ...
break;
case ZOCEnumValue1:
// ...
break;
case ZOCEnumValue2:
// ...
break;
}
- 枚举类型
当使用 enum 的时候,建议使用
NS_ENUM()
定义,因为它有更强大的类型检查和代码补全功能
typedef NS_ENUM(NSUInteger, ZOCMachineState) {
ZOCMachineStateNone,
ZOCMachineStateIdle,
ZOCMachineStateRunning,
ZOCMachineStatePaused
};
分类
推荐在 category 方法前加上自己的小写前缀以及下划线,比如- (id)zoc_myCategoryMethod,这种实践同样被苹果推荐
这是非常必要的。因为如果在扩展的 category 或者其他 category 里面已经使用了同样的方法名,会导致不可预计的后果。实际上,被调用的是最后被加载的那个 category 中方法的实现(译者注:如果导入的多个 category 中有一些同名的方法导入到类里时,最终调用哪个是由编译时的加载顺序来决定的,最后一个加载进来的方法会覆盖之前的方法)
推荐:
@interface NSDate (ZOCTimeExtensions)
- (NSString *)zoc_timeAgoShort;
@end
不推荐:
@interface NSDate (ZOCTimeExtensions)
- (NSString *)timeAgoShort;
@end
总结
- 此规范参照了
禅与 Objective-C 编程艺术
一书,然后根据具体情况做了相应的修改和调整,目前整理的规范都是我们在开发中比较常用的,也是比较容易疏忽的一些细节点,后面我们根据规范执行情况来做些修改调整,想了解更多更全面的规范请参照禅与 Objective-C 编程艺术 - 根据团队执行的难易程度和优先等级,对编码规范目录做了优化调整,越往上的越简单,也是优先需要掌握的规范点
外卖
- 作者React Native开源项目OneM地址(按照企业开发标准搭建框架完成开发的):https://github.com/guangqiang-liu/OneM:欢迎小伙伴们 star
- 作者主页:包含60+篇RN开发相关的技术文章http://www.jianshu.com/u/023338566ca5 欢迎小伙伴们:多多关注,多多点赞
- 作者React Native QQ技术交流群:620792950 欢迎小伙伴进群交流学习
- 友情提示:在开发中有遇到RN相关的技术问题,欢迎小伙伴加入交流群(620792950),在群里提问、互相交流学习。交流群也定期更新最新的RN学习资料给大家,谢谢大家支持!