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<NSString *> *array;
@property(nonatomic,strong) NSMutableDictionary<NSString *,NSString *> *dictionary;

1.插入数组前,需判空。
2.多线程环境下访问可变集合对象,必要时应该加锁保护。
不可变集合(如NSArray)类默认是线程安全的,而可变集合类(如NSMutableArray)不是线程安全的。
3.多线程访问可变集合对象中的元素,应该先对其进行copy,然后访问不可变集合对象内的元素。
4.注意使用enumerateObjectsUsingBlock遍历集合对象中的对象时,关键字return的作用域是使当前的block返回,而非使当前的整个函数体返回。

- (void)touchesBegan:(NSSet<UITouch *> *)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<UITouch *> *)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文件中。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270