一.总体原则
需求是暂时的,只有变化才是永恒的,面向变化编程,而不是面向需求编程。
不要过分追求技巧,降低程序的可读性
简洁的代码可以让bug无处藏身。要写出明显没有bug的代码,而不是没有明显bug的代码
先把眼前的问题解决掉,解决好,再考虑将来的扩展问题。
二.编码规范
1.if表达式写法
推荐:
if (!error) { return success; }
不推荐:
if (!error) return success;
和:
if (!error) return success;
变量和常量比较
推荐:
[myValue isEqual:@42];
不推荐:
[@42 isEqual:myValue];
nil和BOOL值检查
推荐:
if (someObject) { ...if (![someObject boolValue]) { ...if (!someObject) { ...
不推荐:
if (someObject == YES) { ... // Wrongif (myRawValue == YES) { ... // Never do this.if ([someObject boolValue] == NO) { ...
避免嵌套if语句,合理使用return可以避免增加代码复杂度,提高代码可读性。
推荐:
- (void)someMethod { if (![someOther boolValue]) { return; } // Do something important}
不推荐:
- (void)someMethod { if ([someOther boolValue]) { // Do something important } }
2.三元运算符
三元运算符 ? 应该只用在它能让代码更加清楚的地方。 一个条件语句的所有的变量应该是已经被求值了的。
推荐:
result = a > b ? x : y;
不推荐:
result = a > b ? x = c > d ? c : d : y;
当三元运算符的第二个参数(if 分支)返回和条件语句中已经检查的对象一样的对象的时候,下面的表达方式更灵巧:
推荐:
result = object ? : [self createObject];
不推荐:
result = object ? object : [self createObject];
3.Case语句
除非编译器强制要求,括号在 case 语句里面是不必要的。但是当一个 case 包含了多行语句的时候,需要加上括号。
switch (condition) { case 1: // ... break; case 2: { // ... // 多行语句需要加上括号 break; } case 3: // ... break; default: // ... break; }
有时候可以使用 fall-through 在不同的 case 里面执行同一段代码。一个 fall-through 是指移除 case 语句的 “break” 然后让下面的 case 继续执行。
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; }
4.函数
单一原则
一个函数只做一件事,每个函数的职责都应该划分的很明确。
推荐:
dataConfiguration() viewConfiguration()
不推荐:
void dataConfiguration(){ ... viewConfiguration() }
需要对参数的正确性和有效性进行检查
对相同功能进行封装
将函数内部比较复杂的逻辑提取出来作为单独的函数
三.命名规范
1.统一要求
推荐使用长的、描述性的方法和变量名。尽可能遵守Apple命名约定
推荐:
UIButton *settingsButton;
不推荐:
UIButton *setBut;
2.类名
大驼峰式命名:每个单词的首字母都采用大写字母
例如:MainViewController
3.私有变量
小驼峰式命名:第一个单词以小写字母开始,后面的单词的首字母大写。
私有变量:以下划线开头 例如:
NSString *_peopleName
4.属性
小驼峰式命名:第一个单词以小写字母开始,后面的单词的首字母大写。
关键字顺序:原子性、读写权限、内存管理
Block、NSString属性使用copy修饰
5.常量
常量应该以驼峰法命名,并以相关类名作为前缀。
推荐:
static const NSTimeInterval VAReportViewControllerFadeOutTime = 0.4;
不推荐:
static const NSTimeInterval fadeOutTime = 0.4;
推荐使用常量来代替字符串字面值和数字,这样能够方便复用,而且可以快速修改而不需要查找和替换。常量应该用
static
声明为静态常量,而不要用#define
,除非它明确的作为一个宏来使用。
推荐:
static const NSTimeInterval VAReportViewControllerFadeOutTime = 0.4;static NSString * const VAReportViewControllerCellIdentifier = @"VAReportViewControllerCell";
不推荐:
#define VAReportViewControllerFadeOutTime 0.4#define VAReportViewControllerCellIdentifier @"VAReportViewControllerCell"
对于外部可见的常量,在头文件中以这样的形式暴露给外部:
extern NSString *const VAReportViewControllerCellIdentifier;
并在实现文件中为其赋值。
只有公有的常量才需要添加命名空间作为前缀。尽管实现文件中私有常量的命名可以遵循另外一种模式,你仍旧可以遵循这个规则。
6.枚举
命名规则和类的命名一致。
枚举内容的命名需要以该枚举类型名称开头。
NS_ENUM 定义通用枚举 NS_OPTIONS 定义位移枚举
例如:
typedef NS_ENUM(NSInteger, UIViewAnimationTransition) { UIViewAnimationTransitionNone, UIViewAnimationTransitionFlipFromLeft, UIViewAnimationTransitionFlipFromRight, UIViewAnimationTransitionCurlUp, UIViewAnimationTransitionCurlDown, };typedef NS_OPTIONS(NSUInteger, UIControlState) { UIControlStateNormal = 0, UIControlStateHighlighted = 1 << 0, UIControlStateDisabled = 1 << 1, UIControlStateSelected = 1 << 2 };
7.指定初始化方法和间接初始化方法
Objective-C 有指定初始化方法(Designated Initializer)和间接(Secondary Initializer)初始化方法的观念。designated 初始化方法是提供所有的参数,secondary 初始化方法是一个或多个,并且提供一个或者更多的默认参数来调用 designated 初始化的初始化方法。
在你希望提供你自己的初始化函数的时候,需要遵循下列原则:
正确的例子:
定义你的 Designated Initializer,确保调用了直接父类的Designated Initializer。
重写直接父类的Designated Initializer。调用你的新的Designated Initializer。
为新的Designated Initializer 进行文档注释。
#import <UIKit/UIKit.h>@interface VAMessageTableViewCell : UITableViewCell- (__kindof VAMessageTableViewCell *)initWithName:(NSString *)name date:(NSDate *)date identifier:(NSString *)identifier VA_DESIGNATED_INITIALIZER; - (__kindof VAMessageTableViewCell *)initWithName:(NSString *)name identifier:(NSString *)identifier; - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier VA_UNAVAILABLE_INITIALIZER; - (instancetype)initWithCoder:(NSCoder *)aDecoder VA_UNAVAILABLE_INITIALIZER; - (instancetype)init VA_UNAVAILABLE_INITIALIZER;@end #import "VAMessageTableViewCell.h"@interface VAMessageTableViewCell ()@property (nonatomic,copy) NSString *name;@property (nonatomic,strong) NSDate *date;@end@implementation VAMessageTableViewCell- (VAMessageTableViewCell *)initWithName:(NSString *)name date:(NSDate *)date identifier:(NSString *)identifier { //调用直接父类的designated initializer self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier]; if (self) { _name = name; _date = date; } return self; }//Secondary Initializer- (VAMessageTableViewCell *)initWithName:(NSString *)name identifier:(NSString *)identifier { return [self initWithName:name date:nil identifier:identifier]; }//重写直接父类的Designated Initializer- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { return [self initWithName:nil date:nil identifier:reuseIdentifier]; }//重写直接父类的Designated Initializer- (instancetype)initWithCoder:(NSCoder *)aDecoder { return [self initWithName:nil date:nil identifier:nil]; }@end
其中VA_DESIGNATED_INITIALIZER
和VA_UNAVAILABLE_INITIALIZER
定义如下:
#define VA_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer))#define VA_UNAVAILABLE_INITIALIZER __attribute__((unavailable("please use designated initializer")))
相关宏介绍:
__attribute__((objc_designated_initializer))
:用来修饰类的designated initializer初始化方法,如果修饰的方法里没有调用父类的 designated initializer,编译器会发出警告。
__attribute__((unavailable))
:可以用来修饰变量,方法,类和协议,表明不可用,如果使用,编译器会发出错误。同deprecated,可以添加说明。
8.方法
推荐:
- (__kindof People *)initWithName:(NSString *)name age:(NSInteger)age birthday:(NSDate *)birthday;
不推荐:
-(instancetype)initWithName:(NSString *)name andAge:(NSInteger)age andBirthday:(NSDate *)birthday;
建议所有返回类的实例的类方法和实例方法使用__kindof
,不要使用id
或者instancetype
,原因如下:
id修饰
用id修饰返回值类型,不会在编译时进行类型判断。
返回值类型没有确切提示。
instancetype修饰
虽然会自动识别当前对象的类,但是仍然没有类型提示。
9.通知
当你定义你自己的 NSNotification
的时候你应该把你的通知的名字定义为一个字符串常量,就像你暴露给其他类的其他字符串常量一样。你应该在公开的接口文件中将其声明为 extern
的, 并且在对应的实现文件里面定义。
因为你在头文件中暴露了符号,所以你应该按照统一的命名空间前缀法则,用类名前缀作为这个通知名字的前缀。
同时,用一个 Did/Will 这样的动词以及用 "Notifications" 后缀来命名这个通知也是一个好的实践。
// Foo.hextern NSString * const ZOCFooDidBecomeBarNotification// Foo.mNSString * const ZOCFooDidBecomeBarNotification = @"ZOCFooDidBecomeBarNotification";
四.注释规范
优秀的代码大部分是可以自描述的,我们完全可以用代码本身来表达它到底在干什么,而不需要注释的辅助。
但并不是说一定不能写注释,有以下三种情况比较适合写注释:
公共接口(注释要告诉阅读代码的人,当前类能实现什么功能)
涉及到比较深层专业知识的代码(注释要体现出实现原理和思想)。
容易产生歧义的代码(但是严格来说,容易让人产生歧义的代码是不允许存在的)。
除了上述这三种情况,如果别人只能依靠注释才能读懂你的代码的时候,就要反思代码出现了什么问题。
1.import注释
如果有一个以上的import
语句,就对这些语句进行分组,每个分组的注释是可选的。
// Frameworks#import <QuartzCore>;// Models#import "NYTUser.h"// Views#import "NYTButton.h"#import "NYTUserView.h"
2.属性注释
使用//
注释,在属性之后,用一个空格隔开。
@property (nonatomic,copy) NSString *name; //用户名
3.方法注释
使用Xcode快捷键command
+option
+/
进行注释:
/** 方法描述 @param name 参数描述 @param date 参数描述 @param identifier 参数描述 @return 返回值 */- (__kindof VAMessageTableViewCell *)initWithName:(NSString *)name date:(NSDate *)date identifier:(NSString *)identifier VA_DESIGNATED_INITIALIZER;
4.代码块注释
单行使用//
多行使用/**/
5.#pragma
#pragma mark -
是一个在类内部组织代码并且帮助你分组方法实现的好办法。分离示范:
#pragma mark - Get#pragma mark - Set#pragma mark - Life Cycle- (void)viewDidLoad { [super viewDidLoad]; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; }#pragma mark - Super Class#pragma mark - Event Responder#pragma mark - TableView Delegate DataSource
6.TODO MARK FIXME 标记
//MARK:标记一下 //TODO:通知即将要做 //FIXME:你想要修改的bug
作者:一片姜汁
链接:https://www.jianshu.com/p/b3626ec5f1fd
共同学习,写下你的评论
评论加载中...
作者其他优质文章