23

声明:

  • 本文档面向以Objective-C作为开发语言的iOS团队。
  • 本文档以苹果开发文档为基础,结合网络内容和开发实践进行整理,针对iOS开发进行规范和约定。

1.项目基础

1.1 项目新建信息

  • Product Name:工程名。
  • Team:开发者账号信息,没有选择None,个人开发者账号(含Personal Team),公司/企业账号(如:XXX Co.,LTD),也可以暂时选择None,后续再配置调试。
  • Organization Name:个人开发者账号(自定义名字),公司/企业账号(如:XXX Co.,LTD),此处内容明显体现在代码文件头部注释中。
  • Organization Identifier:个人开发者账号(自定义标识),公司/企业账号(域名反写如:com.xxx)。
  • Bundle Identifier:自动生成格式为 [Organization Identifier] + [Product Name],也可后续配置时进行修改。
  • Language:根据需要选择Objective-C 或者 Swift。
  • Use Core Data:根据项目情况勾选,如果明确需要请直接勾选,也可后续添加。
  • Include Unit Tests:单元测试,根据需要勾选,也可后续添加。
  • Include UI Tests:UI测试,根据需要勾选,也可后续添加。
  • Source Control:默认git进行版本管理,根据需要勾选,也可后续添加。

1.2 项目初始配置

  • Display Name:应用名称。
  • Deployment Info:系统版本、设备、屏幕方向、状态栏等,
  • Build Active Architecture Only:一般Debug模式YES,Release模式NO。如果Release模式为YES,那么上传AppStore之后会显示大量而具体的兼容设备,Release模式为NO则仅显示模糊的兼容信息。
  • Architecture:默认$(ARCHS_STANDARD)不作修改。
  • Valid Architecture:默认arm64/armv7/armv7s不作修改。模拟器32位处理器是i386架构,模拟器64位处理器是x86_64架构,真机32位处理器是armv7或armv7s架构,真机64位处理器是arm64架构。

1.3 项目文件结构

  • 所有的文件应放在工程中的项目目录下。
  • 项目文件和物理文件需保持一致。
  • Xcode创建的任何组(group)都必须有文件夹映射。
  • 项目文件不仅可以按照业务类型分组,也可以根据功能分组。

2.代码格式规范

2.1 代码注释格式

  • 文件注释:采用Xcode自动生成的注释格式。

    //
    //  AppDelegate.h
    //  项目名称
    //
    //  Created by 开发者姓名 on 2018/6/8.
    //  Copyright © 2018年 公司名称. All rights reserved.
    //
  • import注释:如果有一个以上的import语句,对这些语句进行分组,每个分组的注释是可选的

    // Framework
    #import <UIKit/UIKit.h>
    
    // Model
    #import "WTUser.h"
    
    // View
    #import "WTView.h"
  • 方法注释:Xcode8之后快捷键自动生成(option + command + /)。

    /**
    <#Description#>
    
    @param application <#application description#>
    @param launchOptions <#launchOptions description#>
    @return <#return value description#>
    */
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions;
  • 代码块注释:单行的用 “// + 空格” 开头, 多行用“/* */”。

2.2 代码结构与排版

  • 声明文件:方法顺序和实现文件的顺序保持一致,根据需要用”#pragma mark -“将方法分组。
  • 实现文件:必须用”#pragma mark -“将方法分组。分组前后优先级:Lifecycle方法 > Public方法 > UI方法 > Data方法 > Event方法 > Private方法(逻辑处理等) > Delegate方法 > 部分Override方法 > Setter方法 > Getter方法。

    #pragma mark - Lifecycle
    
    - (instancetype)init {}
    - (void)viewDidLoad {}
    - (void)viewWillAppear:(BOOL)animated {}
    - (void)viewDidAppear:(BOOL)animated {}
    - (void)viewWillDisappear:(BOOL)animated {}
    - (void)viewDidDisappear:(BOOL)animated {}
    - (void)didReceiveMemoryWarning {}
    - (void)dealloc {}
    
    #pragma mark - Public
    
    - (void)refreshData {}
    
    #pragma mark - UI
    
    - (void)initSubViews {}
    
    #pragma mark - Data
    
    - (void)initData {}
    - (void)constructData {}
    
    #pragma mark - Event
    
    - (void)clickButton:(UIButton *)button {}
    
    #pragma mark - Private
    
    - (CGFloat)calculateHeight {}
    
    #pragma mark - UIScrollViewDelegate
    
    - (void)scrollViewDidScroll:(UIScrollView *)scrollView {}
    
    #pragma mark - Override
    
    - (BOOL)needNavigationBar {}
    
    #pragma mark - Setter
    
    - (void)setWindow:(UIWindow *)window {}
    
    #pragma mark - Getter
    
    - (UIWindow *)window {}
    
  • 变量:优先使用属性声明而非变量声明,注意属性修饰符、变量类型、变量之间的间隔。

    @property (strong, nonatomic) UIWindow *window;
  • 点语法:应始终使用点语法来访问和修改属性。
  • 间距要求如下:

    • 一个缩进使用四个空格。
    • 在”-“或者”+“号之后应该有一个空格,方法的大括号和其它大括号始终和声明在同一行开始,在新的一行结束,另外方法之间应该空一行。

    • (BOOL)application:(UIApplication )application didFinishLaunchingWithOptions:(NSDictionary )launchOptions {
      if (door.isClosed) {

      // Do something

      } else {

      // Do something

      }
      return YES;
      }

  • 长度要求如下:

    • 每行代码的长度不应该超过100个字符。
    • 单个函数或方法的实现代码控制在50行以内。
    • 单个文件里的代码行数控制在500~600行之内。

3.代码命名规范

3.1 代码命名基础

  • 最好是既清晰又简短,但不要为简短丧失清晰性,并使用驼峰命名法。
  • 名称通常不缩写,即使名称很长也要拼写完全(禁止拼音),然而可使用少数非常常见的缩写,部分举例如下:

    常用缩写词含义常用缩写词含义
    appapplicationmaxmaximum
    altalternateminminimum
    calccalculatemsgmessage
    allocallocterectrectangle
    deallocdealloctemsgmessage
    initinitializetemptemporary
    intintegerfuncfunction
  • 由于Cocoa(Objective-C)没有C++一样的命名空间机制,需添加前缀(公司名首字母)防止命名冲突,前缀使用2个字符(以下统称项目前缀)。
  • 常见的单词略写:ASCII,PDF,HTTP,XML,URL,JPG,GIF,PNG,RGB

3.2 类和协议命名

  • 类名应明确该类的功能,并且要有项目前缀防止命名冲突。
  • 协议组合一组相关的方法,不关联具体的一个类,使得某些相似类有统一的接口,这种协议的命名应采用动名词形式(ing),例如:NSLocking。
  • 协议组合一些不相关的方法(主要是避免创建多个独立的协议),仅仅关联某一个具体的类(该类是协议的具体体现者),这种协议用该类名命名,例如:NSObject。
  • 委托形式的协议命名为类名加上Delegate,例如:UIScrollViewDelegate。

3.3 变量和属性命名

  • 变量名应前置下划线“_”,属性名没有下划线。
  • 属性本质上是存取方法setter/getter,可进行重写(注意内存管理)。

    @property (strong, nonatomic) UIWindow *window;
    - (void)setWindow:(UIWindow *)window;
    - (UIWindow *)window;
  • 可以适当的对setter/getter进行别名设置。

    @property(nonatomic,getter=isUserInteractionEnabled) BOOL userInteractionEnabled;

3.4 方法和函数命名

  • 方法名和函数名一般不需要前缀,但函数(C语言形式)作为全局作用域的时候最好加上项目前缀。
  •  表示行为的方法名称以动词开头,但不要使用do/does等无实际意义的助动词。
  • 参数前面的单词要能够描述该参数,并且参数名最好能用描述该参数的单词命名。

    - (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
  • 方法中多个参数可以使用适当的介词进行连接。

     // 后续多个参数使用with
     - (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2; 
      //  添加适当介词能够使方法的含义更明确
      - (BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit;
       // 第一个参数用了with,后面的参数不使用with
        - (instancetype)initWithImage:(nullable UIImage *)image highlightedImage:(nullable UIImage *)highlightedImage;
        
  • 只有在方法返回多个值的时候使用get单词进行明确。

    - (void)getLineDash:(nullable CGFloat *)pattern count:(nullable NSInteger *)count phase:(nullable CGFloat *)phase;
  • 方法返回某个对象实例。

    + (instancetype)buttonWithType:(UIButtonType)buttonType;// 类方法创建对象
    + (UIApplication *)sharedApplication;// 单例命名
  • 委托或代理方法命名第一个参数最好能相关某个对象。

    - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath;
  • 私有方法不要以下划线“_“开头,因为系统私有方法保留此方式。
  • 自定义方法和系统方法重名,建议在方法开头加前缀”xx_methodName“。

3.5 常量和宏的命名

  • const常量外部声明:在Objective-C文件中优先采用FOUNDATION_EXTERN和UIKIT_EXTERN,而非C语言中的extern。
  • const常量采用驼峰命名原则。
  • const常量根据作用域适当加上前缀(含项目前缀):可供外部使用需加上相应的类名或者模块前缀,仅文件内部使用需要加上小写字母“k”.
  • 宏定义每个字母采用大写,单词之间用下划线“_”间隔。
  • 宏定义也可根据作用范围加上适当前缀,避免命名冲突。

3.6 枚举的命名

  • 使用枚举来定义一组相关的整数常量,增强代码的可读性。
  • 枚举可根据作用域添加前缀(含项目前缀),格式:[相关类名或功能模块名] + [描述] + [状态]。
  • 建议优先采用Objective-C的声明NS_ENUM和NS_OPTIONS,少采用C语言形式的enum等枚举声明.
  • 枚举定义时需指定None状态,并且其rawValue一般为起始值0。

    // NS_ENUM
    typedef NS_ENUM(NSInteger, UIStatusBarAnimation) {
        UIStatusBarAnimationNone    = 0,
        UIStatusBarAnimationFade    = 1,
        UIStatusBarAnimationSlide   = 2,
    }
    typedef NS_OPTIONS(NSUInteger, UIRemoteNotificationType) {
        UIRemoteNotificationTypeNone    = 0,
        UIRemoteNotificationTypeBadge   = 1 << 0,
        UIRemoteNotificationTypeSound   = 1 << 1,
        UIRemoteNotificationTypeAlert   = 1 << 2,
        UIRemoteNotificationTypeNewsstandContentAvailability = 1 << 3,
    }

3.7 通知命名

  • 外部声明:在Objective-C文件中优先采用FOUNDATION_EXTERN和UIKIT_EXTERN,而非C语言中的extern。
  • 通知的命名一般都是跨文件使用的,需添加项目前缀。

    // [相关联类名或者功能模块名] + [will/Did](可选) + [描述] + Notification
    UIApplicationDidEnterBackgroundNotification       
    UIApplicationWillEnterForegroundNotification      

3.8 类型别名命名

  • 根据作用域添加前缀(含项目前缀),格式:[类名或功能模块名] + [描述]。

4.文件资源命名规范

  • 资源文件命名也需加上项目前缀。
  • 资源文件名全小写,单词之间用下划线“_”间隔。
  • 资源文件命名格式:[项目前缀] + [业务] + [文件名]
  • 图片文件命名格式:[项目前缀] + [业务] + [类型] + [状态]。

    // 常见类型:logol,icon,img
    // 常见状态:normal,selected,highlight
    UIImage *image = [UIImage imageNamed:@"wt_mine_setting_normal"];

5.代码警告处理

  • 注意警告问题的隐蔽性,因此最好修复警告。
  • 警告类型的查看步骤:选中警告 -> 右键Reveal in Log(不编译Reveal in Log是灰色的,因此先编译) ->查看方括号的内容
  • 如果需要忽略警告,建议优先代码push或者pop处理。

    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Warc-retain-cycles"
    // 造成警告的代码
    #pragma clang diagnostic pop
  • 如果警告数量过大,检查警告类型以及必要性,可xcode配置忽略此类型警告。步骤:选中工程 -> TARGETS -> Build Settings -> Other Warning Flags。

    忽略单个和全局配置稍有差别,如下举例:
    push/pop    Other Warning Flags
    -Wformat —-> -Wno-format 
    -Wunused-variable —-> -Wno-unused-variable 
    -Wundeclared-selector —-> -Wno-undeclared-selector 
    -Wint-conversion —-> -Wno-int-conversion
  • 也可以在pch等大范围作用域的头文件中添加代码来忽略后续警告:#pragma clang diagnostic ignored “警告名称” 。

6.外部库文件引入

  • 库文件引入最好把警告处理掉。
  • 库文件引入优先采用CocoaPods引入,并且指定版本号。
  • 源文件方式需引入文件到工程目录下。
    图例
  • 源文件方式需注意有无版本说明信息(可能在README文件中,也可能在某个.h头文件中,又或者有Version文件)没有时需在库文件目录下新增版本说明文件,

7.代码版本管理

  • 版本管理工具:svn 或 git。
  • svn文件管理配置:目录~/.subversion打开config文件全局配置global-ignore,所有的仓库都会受到影响,而svn:ignore只影响仓库目录。
  • git文件管理配置:.gitignore_global为全局配置,而仓库目录下的.gitignore文件仅注明本仓库被git忽略的文件,常见语法如下:

    • 井号(#)用来添加注释用的,比如 "#注释"。
    • build/ : 星号()是通配符,build/*则是要说明要忽略 build 文件夹下的所有内容。
    • *.pbxuser : 表示要忽略后缀名为.pbxuser的文件。
    • !default.pbxuser : 感叹号(!)是取反的意思,*.pbxuser 表示忽略所有后缀名为.pbxuser的文件,如果加上!default.pbxuser则表示,除了default.pbxuse忽略其它后缀名为pbxuse的文件。
  • 提交信息规范:

    • BUG类型为“Fix + [BUG编号] + [BUG描述]”。
    • 任务类型为“Done + [任务编号] + [任务描述]”。
    • 任务中间态为“Doing + [任务编号] + [任务描述]”。
    • 引入类库为“import + [类库名]”。

8.构建和分发

  • 手动构建:Xcode界面化构建、xcodebuild终端命令构建。
  • 自动化构建:Jenkins+Fastlane、xcodebuild脚本执行 。
  • 内测分发渠道:fir.im蒲公英等。
  • 线上分发渠道:AppStore。

windtersharp
216 声望22 粉丝

看到巨人的肩膀!