代码结构
#pragma mark - life cycle 生命周期
#pragma mark - notification 通知
#pragma mark - action 事件处理
#pragma mark - Delegate
#pragma mark - UI 界面搭建
#pragma mark - setter & getter
#pragma mark - other
命名规则
1.常规
- 格式:驼峰式;
- 见名知义;杜绝拼音;不过度缩写
2.方法命名
- 禁止在方法名前加下划线“ _ ”;
- 如方法返回某个属性,则直接以属性名作为方法名。无需在方法名前加"get";
- 只有当方法间接的返回对象或数值,才有必要在方法名中使用"get",这种格式只适用于返回多个数据项的情况;如:
// 通过传入指针,来获得多个值
- (void)getLineDash:(float *)pattern count:(int*)count phase:(float *)phase;
// NSURLCache (NSURLSessionTaskAdditions)中声明的方法
- (void)getCachedResponseForDataTask:(NSURLSessionDataTask *)dataTask completionHandler:(void (^) (NSCachedURLResponse * __nullable cachedResponse))completionHandler;
- 方法中的所有参数前都应加关键字,描述参数的意义;
- 如果当前子类创建的方法比从父类继承来的方法更加具体明确。本身提供的方法更具有针对性。则不该重写类本身提供的方法。而是应该单独的提供一个方法,并在新的方法后面添加上必要的关键参数;
// UIView提供的方法
- (instancetype)initWithFrame:(CGRect)frame
// 更具针对性的方法
- (instancetype)initWithFrame:(CGRect)frame mode:(int)aMode cellClass:(Class)factory Id numberOfRows:(int)rows numberOfColumns:(int)cols;
- 私有方法命名,可加前缀xx_。如
xx_SaveUserData;
3.代理方法命名
- 使用did或will、should情态动词
- 以触发消息的对象名开头,省略类名前缀并且首字母小写
- (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(int)row;
- (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename;
- 除非delegate方法只有一个参数,即触发delegate方法调用的delegating对象,否则冒号是紧跟在类名后面的
- (BOOL)applicationOpenUntitledFile:(NSApplication *)sender;
4.Category命名
- 避免category中的方法覆盖系统方法。
- 可以使用前缀来区分系统方法和category方法。
- 如果一个类比较复杂,建议使用category增加方法。
5.属性命名
/** 订单总价 */
@property (nonatomic, assign) CGFloat totalPrice;
6.类名
- 应该由两部分组成,前缀+名称。
7.协议名
- 有时protocol只是声明了一堆相关方法,并不关联类。这时protocol使用ing形式,以和class区分开来。如:
NSLocking
- 如果proctocol还关联了某个类。这时命名取决于关联的类,然后再后面再加上protocol或delegate用于显示的声明这是一份协议。 如:
UITableViewDeleagte
8.通知命名
- 必须是全局的常量形式
- 使用“will”或者“did”这样的助动词,命名格式:
[相关类名] + [Did | Will] + [UniquePartOfName] + Notification
NSApplicationDidBecomeActiveNotification
NSTextViewDidChangeSelectionNotification
- 在发送通知时要传递信息,请使用userInfo,而不是object。因为object通常是指发出通知的对象。
9.常量命名
对于int常量,使用枚举创建;对于float常量,用const修饰符创建。
const float viewWidth
如果一个整型常量和其他常量不相关,使用const来创建,否则,使用枚举类型表示一组相关的整型常量。
10.枚举定义
typedef NS_ENUM(NSUInteger,myKeyBoradType){
KeyBoardTypeDefault = 0,
KeyBoardTypeNumber,
KeyBoardTypeEmail
}
编码规范
1. dealloc
-
dealloc
方法应该放在实现文件的最上面。 -
init
应该直接放在dealloc
方法的下面。 - 不要忘记在
dealloc
方法中移除通知和KVO。 - 和
init
方法一样,禁止在dealloc
方法中使用点语法访问属性。
2.Block
调用block时需要对block判空。
注意block潜在的引用循环。
UI 规范
1.如果想要获取window请使用[[UIApplication sharedApplication] keyWindow];
2.使用到UITableView,UICollectionView
,要在 dealloc
方法里手动的把对应的 delegate, dataSouce
置为 nil。
3.获取视图的x、y、width、height,请使用CGRectGet
方法:
CGRect frame = self.view.frame;
CGFloat x = CGRectGetMinX(frame);
CGFloat width = CGRectGetWidth(frame);
反对以下写法:
CGRect frame = self.view.frame;
CGFloat x = frame.origin.x;
CGFloat width = frame.size.width;
IO规范
尽量少用NSUserDefaults
。synchronize
方法会block住当前线程,直到所有的内容都写进磁盘。如果内容过多,重复调用会影响性能。
一些经常被使用的文件建议做好缓存,避免重复的IO操作。
集合规范
0.集合类使用泛型来指定对象的类型。
@property(nonatomic,copy) NSArray *array;
@property(nonatomic,strong) NSMutableDictionary *dictionary;
1.插入数组前,需判空。
2.多线程环境下访问可变集合对象,必要时应该加锁保护。
不可变集合(如NSArray)类默认是线程安全的,而可变集合类(如NSMutableArray)不是线程安全的。
3.多线程访问可变集合对象中的元素,应该先对其进行copy,然后访问不可变集合对象内的元素。
4.注意使用enumerateObjectsUsingBlock
遍历集合对象中的对象时,关键字return的作用域是使当前的block返回,而非使当前的整个函数体返回。
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSArray *array = [NSArray arrayWithObject:@"1"];
[array enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
// excute some code...
return;
}];
NSLog(@"fall through");// 依然会执行到这里
}
4.如使用NSMutableDictionary
作为缓存,建议用NSCache
代替。
分支语句规范
1.条件语句必须要加大括号{}
// 建议
if (!error) {
return success;
}
// 不建议
if (!error) return success;
2.判断条件多于3个必须用参数分割成多个有意义的bool变量。
3.永远不要直接和 YES 和 NO进行比较。因为 YES 被定义为 1,而 BOOL 可以多达 8 位。
// 建议
if (isAwesome)
if (![someObject boolValue])
// 禁止这样做
if ([someObject boolValue] == NO) { }
if (isAwesome == YES) { }
4.不要把真正的逻辑写到大括号内。
// 不建议
- (void)someFuncWith:(NSString *)parameter {
if (parameter) {
[self doSomething];
}
}
// 建议
- (void)someFuncWith:(NSString *)parameter {
if (!parameter) {
return;
}
[self doSomething];
}
5.使用switch...case...
语句的时候,不要丢掉default:
。除非switch
枚举。
每个case都要添加break关键字。
懒加载
适合的场景:
一个对象的创建依赖于其他对象。
一个对象在整个app过程中,可能被使用,也可能不被使用。
一个对象的创建需要经过大量的计算,或者比较消耗性能。
如果都不符合以上条件,请不要使用懒加载。
懒加载中不应该有其他的不必要的逻辑性代码。如果有,请把那些逻辑性代码放到合适的地方。
多线程规范
0.禁止在子线程中进行UI操作。
1.禁止使用GCD的dispatch_get_current_queue()
获取当前线程信息。
2.禁止dispatch_sync(dispatch_get_main_queue(), block);
会死锁。
3.在主线程中禁止进行同步网络资源读取,使用NSURLSession进行异步获取。
4.对剪贴板的读取必须要放在异步线程处理。因为读取大量的内容,导致读取线程被长时间阻塞。
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
if (pasteboard.string.length > 0) { //这个方法会阻塞线程
NSString *text = [pasteboard.string copy];
[pasteboard setValue:@"" forPasteboardType:UIPasteboardNameGeneral];
if (text == nil || [text isEqualToString:@""]) {
return ;
}
dispatch_async(dispatch_get_main_queue(), ^{
[self processShareCode:text];
});
}
});
内存管理
1.请慎重使用单例,避免产生不必要的常驻内存。
2.除非你清除的知道自己在做什么,否则不建议将UIView对象加入到NSArray、NSDictionary、NSSet
中。如有需要,可添加到NSMapTable 、 NSHashTable
。
前者相当于weak的
NSMutableArray
;后者相当于weak的NSMutableDictionary
。需注意元素提前释放。
因为NSArray、NSDictionary、NSSet
会对加入的对象做strong引用(即使你把加入的对象进行了weak)。
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
WSObject *object = [WSObject new];
NSHashTable *hashTable = [NSHashTable weakObjectsHashTable];
[hashTable addObject:object];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"count = %ld",hashTable.count);
});
}
// 打印结果:
// dealloc
// count = 1
延迟调用规范
方法performSelector:withObject:afterDelay:
要在有Runloop的线程里调用,否则调用无法生效。
异步线程默认是没有runloop的,除非手动创建;而主线程是系统会自动创建Runloop的。
注释规范
【必须】如果方法、函数、类、属性等需要提供给外界或者他人使用,必须要加注释说明。
【必须】如果你的代码以SDK的形式提供给其他人使用,那么接口的注释是必须的。必须对暴露给外界的所有方法、属性、参数加以注释说明。
【建议】注释应该说明其作用以及注意事项(如果有)。
【建议】因为方法或属性本身就具有自我描述性,注释应该简明扼要,说明是什么和为什么即可。
类的设计规范
1.尽量减少继承,类的继承关系不要超过3层。可以考虑使用category、protocol来代替继承。
【建议】把一些稳定的、公共的变量或者方法抽取到父类中。子类尽量只维持父类所不具备的特性和功能。
【建议】.h文件中尽量不要声明成员变量。属性尽量声明为只读。
【建议】.h文件中只暴露出一些必要的类、公开的方法、只读属性;私有类、私有方法和私有属性以及成员变量,尽量写在.m文件中。