iOS新项目开发规范梳理(备忘)

工程组 - iOS项目开发规范

  1. 语言

    • OC
  2. 工具

    • 编辑器:XCode 11.4(保持最新)
    • 托管平台:Git(Sourcetree客户端)
    • 三方类库管理:CocoaPods
  3. 代码

    • 项目框架设计模式:MVP

    • UI界面开发:Interface Builder和纯代码混合开发

    • 网络请求框架:AFNetworking

    • 图片请求框架:SDWebImage

    • 弹窗提示和加载圈效果:MBProgressHUD

    • 刷新加载:MJRefresh

    • 工程结构规范:

      • 项目中物理文件和Xcode项目文件保持同步,
      • 代码要按照类型进行分组,也要根据业务功能进行分组
      • 工程中一般包含文件夹但不限于:category(分类)、util/helper(工具类)、resource(资源)、const(常量)、third(第三方)、define等。
    • 注释规范:

      • 如果方法、函数、类、属性等需要提供给外界或者他人使用,必须要加注释说明。
      • 如果你的代码以SDK的形式提供给其他人使用,那么接口的注释是必须的。必须对暴露给外界的所有方法、属性、参数加以注释说明。
      • 注释应该说明其作用以及注意事项
      • 因为方法或属性本身就具有自我描述性,注释应该简明扼要,说明是什么和为什么即可。
    • 常见代码编写规范:

      • 类:每一个类文件名称,我们按照规范加上前缀,默认是以公司缩写作为前缀ZY(表示Zoneyet);减少类的继承层数,不超过3层,可以考虑使用category、protocol来代替继承;.h文件中只暴露出一些必要的类、公开的方法、只读属性;私有类、私有方法和私有属性以及成员变量,写在.m文件中

      • 变量:变量命名使用有清晰描述性的方法名,如UIButton *deleteButtom,不要缩写或省略单词。(注:保证可读性的同时,for循环中遍历出来的对象或者某些方法的参数可以缩写)

      • 常量:用常量来代替字符串字面值和数字,方便复用,可以快速修改而不需要查找和替换,用const来创建;

      • 方法:方法名与方法类型 (-/+)之间应该以空格间隔,方法的命名也应该具有自我描述性,方法参数之前的单词要能描述参数的意义;

      • 懒加载:当对象的创建依赖于其他对象,可能被使用也可能不被使用,并且这个对象创建需要经过大量计算或者比较消耗性能事,尽量重写 getter 方法以延迟实例化,而不是在 init 方法里给对象分配内存。

      • 条件语句:不省略大括号(即使条件语句体中只有一行代码),条件语句中的条件不要直接和 YES 和 NO进行比较(如:禁止写if(xx == YES),或者if(xx == NO) ),nil 解析为 NO,YES 被定义为 1,所以没有必要在条件中与它进行比较;

      • 枚举:使用 enum 时,使用NS_ENUM()定义,因为它有更强大的类型检查和代码补全功能;

      • 对象判等规范:如果不太清楚要比较的数据源的类型,使用isEqual:,因为isEqual会对参数进行类型检查,如果数据类型和receiver(方法调用者)类型不一致,它会返回NO。如果我们知道参数的确切类型,那么可以使用类似于isEqualToString:或者isEqualToArray:这样的方法,因为性能更好。

      • 单例:慎重使用单例,避免产生不必要的常驻内存,单例初始化方法中尽量保证单一职责,尤其不要进行其他单例的调用。尽量是保证在全局中只有一份的对象,或者需要多线程访问的对象再使用单例。

      • 委托:以触发消息的对象名开头,省略类名前缀并且首字母小写,如:

        - (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(int)row;
        - (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename;
        
      • 分类(延展):在 category 方法前加上自己的小写前缀以及下划线,避免在其他category中使用同名方法,例如:

        @interface NSDate (ZYTimeExtensions)
        - (NSString *)zy_timeAgoShort;
        @end
        
      • 组织代码:使用#pragma mark - 来做类内部代码组织以及方法分组。

    • IO规范:尽量少用NSUserDefaults,[[NSUserDefaults standardUserDefaults] synchronize] 会block住当前线程,知道所有的内容都写进磁盘,如果内容过多,重复调用的话会严重影响性能。建议一些经常被使用的文件做好缓存,避免重复的IO操作,只在合适的时候再进行持久化操作。

    • 延迟调用规范:performSelector:withObject:afterDelay:要在有Runloop的线程里调用,否则调用无法生效,异步线程中默认没有runloop,可手动创建后在进行延迟调用。

    • 工程公共工具类文件夹

      • 创建Define文件夹,存放内容如下:

        • DataDefine.h文件:

          存放宏定义数据状态码,如:

          #define ERR_CODE_USER_LOGIN_USER_NOT_EXIST        (14)          //用户不存在
          #define ERR_CODE_USER_LOGIN_PW_ERR                (14)          //密码错误
          #define ERR_CODE_USER_CODE_MOBILE_EXIST           (4)           //手机号已注册
          #define ERR_CODE_USER_CODE_MOBILE_NOT_EXIST       (1010)        //手机号不存在
          #define ERR_CODE_USER_RESET_PW_TOKEN_INVALID      (1018)        //token失效
          ......
          

          存放一些错误文本信息,如:

          // Error info
          #define ErrInfo_Network                    @"网络连接失败,请检查您的网络连接"
          #define ErrInfo_ServerInternal     @"服务器内部错误"
          #define ErrInfo_Data                     @"数据错误"
          ......
          

          存放固定秘钥信息,如:

          #define API_SIGN_KEY   @"%r3fd)&ds~23ds1+;xJL"
          #define RC4_SIGN_KEY   @"%r3fd)&ds~23ds1+;xJL"
          ......
          
        • HTTPDefine.h文件:

          存放请求参数配置,如:

          //请求超时时间
          #define TIMEINTERVAL_HTTP_URL_REQUEST      (30.0F) 
          #define TIMEINTERVAL_PIC_URL_REQUEST       (60.0F) 
          //缓存超时时间
          #define TIMEINTERVAL_HTTP_EXPIRE1          (60*60)
          #define TIMEINTERVAL_HTTP_EXPIRE2          (60*60*24) //(60*60*24) //(1)
          #define TIMEINTERVAL_PIC_EXPIRE               (1)       //(60*60*24) //(1)
          

          存放通用请求URL,通用请求参数名称,如:

          // Base URL
          #define URL_TEST_BASE         @"http://121.199.38.85/mishiclient/api.php?act="                  
          #define URL_TEST_BASE_POST    @"http://121.199.38.85/mishiclient/api.php?"              
          #define URL_UPGRATE                  @"soft_upgrade"   // Root
          #define URL_LOGIN                                @"login"          // Login
          #define URL_ORDER_LIST                         @"get_order_list" // Order List
          
        • UIDefine.h文件:

          存放UI界面常用数据信息,如:

          #define SCREEN_WIDTH [UIScreen mainScreen].bounds.size.width
          #define SCREENH_HEIGHT [UIScreen mainScreen].bounds.size.height
          #define MAIN_SCREEN [UIScreen mainScreen]
          #define NAV_HEIGHT (44.0F)
          

          存放常用的颜色设置:

          //随机颜色
          #define MyRandomColor [UIColor colorWithRed:arc4random_uniform(256)/255.0 green:arc4random_uniform(256)/255.0 blue:arc4random_uniform(256)/255.0 alpha:1.0]
          //设置r g b颜色
          #define RGBColor(r, g, b) [UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:1.0]
          #define RGBAColor(r, g, b, a) [UIColor colorWithRed:(r)/255.0 green:(r)/255.0 blue:(r)/255.0 alpha:a]
          // clear背景颜色
          #define GClearColor [UIColor clearColor]
          

          自定义NSLog:

          //项目开发中,需在很多地方Log,为方便发布的时要去掉Log,可进行自定义
          #ifdef DEBUG
          #define debugLog(...) NSLog(@"%s 第%d行 \n %@\n\n",__func__,__LINE__,[NSString stringWithFormat:__VA_ARGS__])
          #else
          #define debugLog(...)
          #endif
          

        ​ 等等。

      • Utils类

        • 存放常用的项目通用方法类Utils,如:

          // 验证邮箱
          + (BOOL)validateEmail:(NSString *)candidate {
              NSString *emailRegex = @"[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}";
             NSPredicate *emailTest = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", emailRegex];
              return [emailTest evaluateWithObject:candidate];
          }
          // 验证手机号
          + (BOOL)validatePhoneNumber:(NSString *)checkString{
              /**
               * 手机号码
               * 移动:134[0-8],135,136,137,138,139,150,151,157,158,159,182,187,188
               * 联通:130,131,132,152,155,156,185,186
               * 电信:133,1349,153,180,189
               */
              NSString * MOBILE = @"^1(3[0-9]|5[0-35-9]|8[0235-9])\\d{8}$";
              /**
               10         * 中国移动:China Mobile
               11         * 134[0-8],135,136,137,138,139,150,151,157,158,159,182,187,188
               12         */
              NSString * CM = @"^1(34[0-8]|(3[5-9]|5[017-9]|8[278])\\d)\\d{7}$";
              /**
               15         * 中国联通:China Unicom
               16         * 130,131,132,152,155,156,185,186
               17         */
              NSString * CU = @"^1(3[0-2]|5[256]|8[156])\\d{8}$";
              /**
               20         * 中国电信:China Telecom
               21         * 133,1349,153,180,189,181
               22         */
              NSString * CT = @"^1((33|53|8[09])[0-9]|349)\\d{7}$";
              /**
               25         * 大陆地区固话及小灵通
               26         * 区号:010,020,021,022,023,024,025,027,028,029
               27         * 号码:七位或八位
               28         */
              // NSString * PHS = @"^0(10|2[0-5789]|\\d{3})\\d{7,8}$";
              
              NSPredicate *regextestmobile = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", MOBILE];
              NSPredicate *regextestcm = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", CM];
              NSPredicate *regextestcu = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", CU];
              NSPredicate *regextestct = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", CT];
              if (([regextestmobile evaluateWithObject:checkString] == YES)
                  || ([regextestcm evaluateWithObject:checkString] == YES)
                  || ([regextestct evaluateWithObject:checkString] == YES)
                  || ([regextestcu evaluateWithObject:checkString] == YES))
              {
                  return YES;
              }
              else
              {
                  return NO;
              }
          }
          //检查密码中的特殊字符
          + (BOOL)containSpecialCharacter:(NSString *)str
          {
             //***需要过滤的特殊字符:~¥#&*<>《》()[]{}【】^@/£¤¥|§¨「」『』¢¬ ̄~@#¥&*()——+|《》$_€。
              NSRange specialCharacterRange = [str rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:@"~¥#&*<>《》()[]{}【】^@/£¤¥|§¨「」『』¢¬ ̄~@#¥&*()——+|《》$_€"]];
              if (NSNotFound == specialCharacterRange.location) {
                  return NO;
              }
              return YES;
          }
          

          等等

      • BaseCode文件夹

        通过OC语言的继承机制,利用已有的数据类型来定义新的数据类型,新的数据类型不仅拥有新定义的成员,还拥有旧的成员。

        项目中可包含的项目基类不仅限于:

        1. 继承于UITabBarController,包含的功能:

          • 设置tabBar的样式
          • 初始化tabBarController控制的controllers
          • 实现UITabBarControllerDelegate协议方法,在方法中实现项目通用逻辑,例如在用户第二次点击tabItem的时候,是否需要刷新界面的情况
          • 注册并监听tabItem切换的消息
        2. 继承于UINavigationController

          • 设置navigationBar的样式,可重写+ (void)initialize方法,在方法中定制样式来满足项目中多个UINavigationController的navigationBar样式一致,如果多个不同样式,可通过isKindOfClass来判断区分使用

          • 设置返回手势

            navigationController: didShowViewController: animated:

        3. 继承于UIViewController

          • 界面的背景颜色设置

          • 消息提示的方法:加载成功/失败,提交成功/失败,网络异常等。

        4. 继承于UITableViewCell/UICollectionViewCell:

          • 在基类中写传值方法,子类继承时,直接实现即可:

            - (void)configureWithItem:(id)item;
            
          • 不同的页面可能会重复的利用一种cell 或者稍微变化, 这时只需再创建第二个基类, 可以将控件暴露在.h文件中,如果是传值的话, 就在.m中重写父类方法;更改frame的话, 可以在layoutSubviews里面更改,(使用[super layoutSubviews])

        5. 自定义视图,继承UIView,对于一些列表视图(tableView/CollectionView)的头视图、轮播图、时间选择器等, 需要自定义的视图, 需要传值的, 可以像cell一样,先写一个传值的方法备着。其他的视情况而定。

        6. 继承于系统控件,比如继承于UILabel/UIButton,自定义一些UI效果

        7. 继承于NSObject

          • 用作model类,数据解析时, 有些属性以及容错方法都是重复率比较高的,可以创建一个基类。但是最好确定每个model都有这个属性再建立基类,如果没有,可以只写容错方法。

以上内容有些借鉴网络,如有侵权,请联系删除,qq: 772081405。

你可能感兴趣的:(iOS新项目开发规范梳理(备忘))