美文网首页
iOS开发规范

iOS开发规范

作者: Arthur澪 | 来源:发表于2020-04-17 14:07 被阅读0次

    代码结构

    #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文件中。

    相关文章

      网友评论

          本文标题:iOS开发规范

          本文链接:https://www.haomeiwen.com/subject/kgivsftx.html