OC篇
一. 核心原则
原则一:代码应该简洁易懂,逻辑清晰
因为软件是需要人来维护的。这个人在未来很可能不是你。所以首先是为人编写程序,其次才是计算机:
- 不要过分追求技巧,降低程序的可读性。
- 简洁的代码可以让bug无处藏身。要写出明显没有bug的代码,而不是没有明显bug的代码。
原则二:面向变化编程,而不是面向需求编程。
需求是暂时的,只有变化才是永恒的。
本次迭代不能仅仅为了当前的需求,写出扩展性强,易修改的程序才是负责任的做法,对自己负责,对公司负责。
原则三:先保证程序的正确性,防止过度工程
过度工程(over-engineering):在正确可用的代码写出之前就过度地考虑扩展,重用的问题,使得工程过度复杂。
引用《王垠:编程的智慧》里的话:
先把眼前的问题解决掉,解决好,再考虑将来的扩展问题。
先写出可用的代码,反复推敲,再考虑是否需要重用的问题。
先写出可用,简单,明显没有bug的代码,再考虑测试的问题。
为解决程序员命名歧义。统一Model命名:入参:XxxParam;出参:XxxVO
1.每一行的字数限制
80个字数限制
xcode设置 屏幕左上角Xode->Perference->Text Editing->"Page guide at column: 80" 打上勾
每一行的代码尽量不要超出80个字的长度,超出的回车排版, 方法名的冒号对齐
2.命名规范
-
2.1类命名
首字母大写,每个单词首字母大写(大驼峰命名法)
尽量使用能够反映类功能的名词短语
eg:UserManage ,UserData等
UIKit里的UI界面部分
控件类型直接使用尾端的驼峰单词
eg:
UIView -> xxxView
UViewController -> xxxViewController
UIButton -> xxxButton
UILabel -> xxxLabel
UIImageView -> xxxImageView
UITableView -> xxxTableView
UITableViewCell -> xxxCell
UIAlertView -> xxxAlertView
UIScrollView -> xxxScrollView
类名过长,取中间的单词作为尾端
eg:
UIActivityIndicatorView -> xxxActivity
UIPickerView -> xxxPicker
UIProgressView -> xxxPorgress
分类(类别)命名
与类命名相同,此外需添加要扩展的类名"+"
eg:
NSString+Login UIView+XIB 等
协议(委托)命名
与类命名相同,使用Delegate或者DataSource作为后缀
eg:
UserManager 对应 UserManagerDelegate
-
2.2方法命名
-
首字母小写,之后每个单词首字母都大写(小驼峰法命名法)
方法名使用动词短语
如果该方式对内部使用的在前面加 "_"
eg: - (void) _loadData{}
如果该方法对外使用不需要加“_”
eg: - (void)viewDidLoad{}
-
2.3变量命名
首字母小写,之后每个单词首字母都大写
具有足够的说明性
成员变量不需要添加“_”前缀
成员变量添加“_”前缀
eg:NSMutableDictionary *_dataDic;
格式化代码
1> 指针
定义一个对象时,指针 "*" 靠近变量
例子: NSString *userName;
2> 方法的声明和定义
在 - 、+ 和 返回值 之间留一个空格,方法名和第一个参数之间不留空格
可变数组或字典变量名
NSMutableDictionary *dataDicM 后加‘M’表示可变 ,必须写
NSDictionary *dataDicN 后加‘N’表示不可变, “N”可不写
常量(预定义,局部常量等)使用小写k开头的驼峰法
eg:#define kUpdateUserInfoNote @"kUpdateUserInfoNote"
可变用strong 不可变用copy修饰
宏 3种情况
全部大写,单词间用 _ 分隔。[不带参数]
例子: #define THIS_IS_AN_MACRO @"THIS_IS_AN_MACRO"
以字母 k 开头,后面遵循大驼峰命名。[不带参数]
例子:#define kWidth self.frame.size.width
小驼峰命名。[带参数]
\#define getImageUrl(url) [NSURL URLWithString:[NSString stringWithFormat:@"%@%@",kBaseUrl,url]]
枚举的内容使用 名称_小内容 加上注释
eg:typedef NS_ENUM(NSInteger, Code) { Code_Success = 200, //成功 Code_Fail = 201, //失败}
eg:typedef NS_ENUM(NSInteger, Code) {
Code_Success = 200, //成功
Code_Fail = 201, //失败
}
IBAction 方法
如果是Button时间,方法名后缀使用Action,如果是内部使用,前面添加'_'
-
2.4图片命名
使用英文,全部小写,单词中间"_"隔开
添加UI模块名作为前缀,避免冲突
图片存放在 Images.xcassets下eg:bar_title icon_coin
注不经常变动的放在 Images.xcassets
经常变动的放在 ChangeImages.xcassets
-
2.5注释
优秀的代码大部分是可以自描述的,我们完全可以用程代码本身来表达它到底在干什么,而不需要注释的辅助。
但并不是说一定不能写注释,有以下三种情况比较适合写注释:
公共接口(注释要告诉阅读代码的人,当前类能实现什么功能)。
涉及到比较深层专业知识的代码(注释要体现出实现原理和思想)。
容易产生歧义的代码(但是严格来说,容易让人产生歧义的代码是不允许存在的)。
除了上述这三种情况,如果别人只能依靠注释才能读懂你的代码的时候,就要反思代码出现了什么问题。
最后,对于注释的内容,相对于“做了什么”,更应该说明“为什么这么做”。
在方法内部注释的地方使用 //即可
在方法上或属性注销的要用文档注释 com+sh+.
文件下的方法区域分类,使用#pragma mark -,可以把在文件路径下的方法分类并标记
eg:#pragma mark - Http
/// 学生
@property (nonatomic, strong) Student *student;
/**
* @brief 登录验证
*
* @param personId 用户名
* @param password 密码
* @param complete 执行完毕的block
*
* @return
*/
+ (void)loginWithPersonId:(NSString *)personId password:(NSString *)password complete:(void (^)(CheckLogon *result))complete{
//注释
if(I < 1){
}
}
-
2.6代码优化
避免相同的代码段在多个地方出现相同的代码,必须归纳出来并且用一个类封装起来
语句嵌套层次不得超过3层,超出的必须抽取出中间函数
g:for,while循环 if,do等
及时删除或注释掉无用的代码
确定不使用的代码应该删除
-
2.7.h文件
import "xxxx.h" 部分头文件时候,如果只是内部使用,请放在.m文件下,.h使用@class xxxx 声明该类即可
文件下有声明代理,把@protocol代理放在@interface 上面
代理方法必须包含该类名
eg:
@class ImageOperation;
@protocol ImageOperationDelegate<NSObject>
- (void) operation:(ImageOperation *)op didLoadImage:(UIImage *)image;
@end
@interface ImageOperation : NSOperation
@end
-
2.8.m文件
@implementation ViewController 上方使用 @interfaceViewController()
@end
使用部分代理,并且代理不被外部使用,请把他们都放在.m的interface ()下
eg:@interface ViewController () <UITableViewDataSource,UITableViewDelegate>
3.系统函数放上面,自定义函数放下面
如常见的viewcontroller
- (void)viewDidLoad{}
- (void)viewDidAppear:(BOOL)animated{}
然后跟着的是Delegate,DataSource
//如UICollectionViewDelegate,UICollectionViewDataSource
- (NSInteger)collectionView:(UICollectionView *)view numberOfItemsInSection:(NSInteger)section{}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{}
......
最后跟自己的方法
- (void) xxxXxxx{}
中间使用#pragma mark-分割,如 Delegate,DataSource 和 自己的的方法
自己的方法
中间使用#pragma mark - 分割 不同的功能
中间使用#pragma mark - 网络请求
中间使用#pragma mark - btnAction
中间使用#pragma mark - NSNotification
中间使用#pragma mark - delegate
中间使用#pragma mark - 设置UI 一定要放在最下方
#pragma mark - Life Cycle Methods
- (instancetype)init
- (void)dealloc
- (void)viewWillAppear:(BOOL)animated
- (void)viewDidAppear:(BOOL)animated
- (void)viewWillDisappear:(BOOL)animated
- (void)viewDidDisappear:(BOOL)animated
#pragma mark - Override Methods
#pragma mark - Intial Methods
#pragma mark - Network Methods
#pragma mark - Target Methods
#pragma mark - Public Methods
#pragma mark - Private Methods
#pragma mark - UITableViewDataSource
#pragma mark - UITableViewDelegate
#pragma mark - Lazy Loads
#pragma mark - NSCopying
#pragma mark - NSObject Methods
符号篇
1大括号
控制语句(if,for,while,switch)中,大括号开始与行尾
函数中,大括号要开始于行首
2运算符
2.1 运算符与变量之间的间隔
2.1.1一元运算符与变量之间没有空格:
!bValue
~iValue
++iCount
*strSource
&fSum
2.1.2二元运算符与变量之间必须有空格
fWidth = 5 + 5;
fLength = fWidth * 2;
fHeight = fWidth + fLength;
for(int i = 0; i < 10; i++)
2.2多个不同的运算符同时存在时应该使用括号来明确优先级
在多个不同的运算符同时存在的时候应该合理使用括号,不要盲目依赖操作符优先级。
- 因为有的时候不能保证阅读你代码的人就一定能了解你写的算式里面所有操作符的优先级。
2 << 2 + 1 * 3 - 4
2 << (2 + 1 * 3 - 4)
3.if语句
- 必须列出所有分支(穷举所有的情况),而且每个分支都必须给出明确的结果。
- 不要使用过多的分支,要善于使用return来提前返回错误的情况
-(id)initWithDictionary:(NSDictionary*)dict error:(NSError)err
{
//方法1. 参数为nil
if (!dict) {
if (err) *err = [JSONModelError errorInputIsNil];
return nil;
}
//方法2. 参数不是nil,但也不是字典
if (![dict isKindOfClass:[NSDictionary class]]) {
if (err) *err = [JSONModelError errorInvalidDataWithMessage:@"Attempt to initialize JSONModel object using initWithDictionary:error: but the dictionary parameter was not an 'NSDictionary'."];
return nil;
}
//方法3. 初始化
self = [self init];
if (!self) {
//初始化失败
if (err) *err = [JSONModelError errorModelIsInvalid];
return nil;
}
//方法4. 检查用户定义的模型里的属性集合是否大于传入的字典里的key集合(如果大于,则返回NO)
if (![self __doesDictionary:dict matchModelWithKeyMapper:self.__keyMapper error:err]) {
return nil;
}
//方法5. 核心方法:字典的key与模型的属性的映射
if (![self __importDictionary:dict withKeyMapper:self.__keyMapper validation:YES error:err]) {
return nil;
}
//方法6. 可以重写[self validate:err]方法并返回NO,让用户自定义错误并阻拦model的返回
if (![self validate:err]) {
return nil;
}
//方法7. 终于通过了!成功返回model
return self;
}
首先判断出各种错误的情况然后提前返回,把最正确的情况放到最后返回。
- 条件过多,过长的时候应该换行
- 条件语句的判断应该是变量在左,常量在右
- 每个分支的实现代码都必须被大括号包围
4.for语句
- 不可在for循环内修改循环变量,防止for循环失去控制。
- 避免使用continue和break。
continue和break所描述的是“什么时候不做什么”,所以为了读懂二者所在的代码,我们需要在头脑里将他们取反。如果出现了continue,只需要把continue的条件取反即可
5.Switch语句
- 如果分子内代码过或定义了变量,每个分支都必须用大括号括起来
- 在Switch语句使用枚举类型的时候,如果使用了default分支,在将来就无法通过编译器来检查新增的枚举类型了。
方法函数
1. 一个函数的长度必须限制在50行以内
通常来说,在阅读一个函数的时候,如果视需要跨过很长的垂直距离会非常影响代码的阅读体验。如果需要来回滚动眼球或代码才能看全一个方法,就会很影响思维的连贯性,对阅读代码的速度造成比较大的影响。最好的情况是在不滚动眼球或代码的情况下一眼就能将该方法的全部代码映入眼帘。
2.一个函数只做一件事(单一原则)
每个函数的职责都应该划分的很明确(就像类一样)。
3对输入参数的正确性和有效性进行检查,参数错误立即返回
4如果在不同的函数内部有相同的功能,应该把相同的功能抽取出来单独作为另一个函数
5将函数内部比较复杂的逻辑提取出来作为单独的函数
6避免使用全局变量,类成员(class member)来传递信息,尽量使用局部变量和参数。
CGRect函数
面向协议编程
- 如果某些功能(方法)具备可复用性,我们就需要将它们抽取出来放入一个抽象接口文件中(在iOS中,抽象接口即协议),让不同类型的对象遵循这个协议,从而拥有相同的功能。
- 因为协议是不依赖于某个对象的,所以通过协议,我们可以解开两个对象之间的耦合
单例
*单例不能作为容器对象来使用
+ (instancetype)sharedInstance
{
static id sharedInstance = nil;
static dispatch_once_t onceToken = 0;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
NSNotification
- 建议将通知的名字作为常量,保存在一个专门的类中:
- 通知必须要在对象销毁之前移除掉
其他
- Xcode工程文件的物理路径要和逻辑路径保持一致。
- 忽略没有使用变量的编译警告
对于某些暂时不用,以后可能用到的临时变量,为了避免警告,我们可以使用如下方法将这个警告消除:
- (NSInteger)giveMeFive
{
NSString *foo;
#pragma unused (foo)
return 5;
}
- 手动明确一个错误
#error Whoa, buddy, you need to check for zero here!
- 手动明确一个警告:
#warning Dude, don't compare floating point numbers like this!
网友评论