iOS开发规范

代码结构

#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规范
尽量少用NSUserDefaultssynchronize方法会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"); // 依然会执行到这里 }

【iOS开发规范】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文件中。

    推荐阅读