iOS 开发规范

导语:

为规范部门 iOS开发,特制定本文档,供各位同仁参考。

语言

使用US英语, 不要使用拼音。e.g:
UIColor *myColor = [UIColor whiteColor];
禁止:
UIColor *yanse = [UIColor whiteColor];

命名规范

一、iOS命名两大原则是:

1. 可读性高(简洁且清晰)。遵循驼峰式样命名法。一般命名较长,比较语言话,能够使开发人员望文生义,一目了然名字的含义和使用方法。e.g:getLoadData

2.防止命名冲突。 每个模块都要加上自己的前缀,可以区分功能范畴并防止不同文件或类之间命名发生冲突。e.g:
相册模块 (PhotoGallery)的代码都以 PG 作为前缀:PGDataManager

二、命名分类

命名分类主要有以下几类:

1.常量的命名。对于常量的命名最好在前面加上字母k作为标记. 如: static const NSTimeInterval kAnimationDuration = 0.3

Tips:
I. 若常量作用域超出编译单元(实现文件), 需要在类外可见时, 使用extern关键字, 并加上该类名作为前缀. 如 extern NSString *const PGThumbnailSize

II.全局常量(通知或者关键字等)尽量用 const 来定义. 因为如果使用宏定义, 一来宏可能被重定义. 二来引用不同的文件可能会导致宏的不同. P.S. 对于#define也添加一下前缀k。

** 2.枚举的命名。**枚举类型命名要加相关类名前缀,并且枚举值命名要加枚举类型前缀。e.g:

    typedef NS_ENUM(NSInteger, UIViewAnimationTransition) {
    UIViewAnimationTransitionNone,
    UIViewAnimationTransitionFlipFromLeft,
    UIViewAnimationTransitionFlipFromRight,
    UIViewAnimationTransitionCurlUp,
    UIViewAnimationTransitionCurlDown,
}; 

3. 变量和对象的命名。给一个对象命名时建议采用 “描述性的单词+变量类型 ” 的方式。e.g:
titleLabel //表示标题的label, 是UILabel类型 confirmButton //表示确认的button, 是UIButton类型
对于BOOL类型, 应加上is前缀, 比如- (BOOL)isEqualToString:(NSString *)aString这样会更加清晰. 如果某方法返回非属性的 BOOL 值, 那么应根据其功能, 选用 has 或 is 当前缀, 如- (BOOL)hasPrefix:(NSString *)aString

4. 类的命名。类名(以及类别、协议名)应首字母大写,并以驼峰格式分割单词 。样式:前缀+描述+类型

注:前缀可以是你的姓名/昵称等主要用于团队开发的时候避免文件名重复
以下是我个人命名方式 e.g:

XJX --- 姓名/昵称
Message --- 描述性本类的功能
Cell/Model --- 类型/模型

1.类的前缀
1)所有类名、枚举、结构、protocol定义时最好加一个统一的标示符,可以是项目缩写,或者个人项目的名称缩写,例如都加上全大写的Hoo(我的姓氏)作为前缀
2)根据功能模块可以在给功能模块的类添加功能模块的名称前缀,如用户中心的 profileViewController.可以命名为HooUCProfileViewController.
2.类的后缀
所有protocol定义时,都加上后缀Delegate 。如,HooRefreshViewDelegate,表示RefreshView的协议;
所有的控制器都加上Controller,所有的通知名都加上Notification。

5.方法命名
方法名应遵守小驼峰原则,首字母小写,其他单词首字母大写,每个空格分割的名称以动词开头。执行性的方法应该以动词开头,小写字母开头,返回性的方法应该以返回的内容开头,但之前不要加get。一些经典的操作应该使用约定的动词,如initWith,insert,remove,replace,add等等。e.g:

- (void)insertModel:(id)model atIndex:(NSUInteger)atIndex;

- (instancetype)arrayWithArray:(NSArray *)array;

6.@Property命名
尽量少用缩写, 使用Apple推荐的短句命名法命名且第一个字母必须小写, 这样子做, 可以省略再注释处理.
比如:
@property (readonly, copy) NSString *decomposedStringWithCanonicalMapping;
禁止:
@property (readonly, copy) NSString *uName;

7.图片命名

1)采用单词全拼,或者大家公认无岐义的缩写(比如:nav,bg,btn等)
2)采用“图标前缀+模块+功能”命名法,模块分为公共模块、私有模块。公共模块主要包括统一的背景。

样式:icon_单词缩写或全拼_功能。eg:

导航条背影图片:icon_nav_bar.png

导航返回按钮:icon_nav_back_normal.png

8.宏命名
宏命名要全部是大写字母,样式“模块+功能描述”,中间用下划线隔开。e.g:
#define BANNER_HEIGHT 215 //广告栏高度

9.通知类名称命名
通知方法中的类名命名方法,采用样式“NNKEY + 功能描述”,所有字母大写。所有通知类名称归类到一个单独的类文档中,如NotificationConstant.m,方便查找。e.g:
NSString *const NNKEY_MANAGEPWD = @"MANAGEPWD"; //验证密码

10.** UserDefault 的key值命名**
UserDefault 的key值命名必须全部大些,以NUDKEY开头,样式为:“NUDKEY+描述性单词”。 e.g:NSString *const NUDKEY_TOKEN = @"TOKEN";//本地密钥
所有的UerDefault的key值集中存放到一个常量文件中,e.g: ArchiveConstant.m文件中。


代码规范

一、iOS代码具备三个原则:

1.可复用。简单来说就是不要写重复的代码, 有重复的部分要尽量封装起来重用.
2.易维护。就是不要把代码复杂化, 不要去写巨复杂逻辑的代码, 而是把复杂的逻辑代码拆分开一个个小的模块, 这也是Do one thing的概念, 每个模块(或者函数)职责要单一,
3.可扩展。要求写代码时要考虑后面的扩展需求。

二、具体注意事项

1、 .h 文件:

  • 不允许import 不必要的头文件,需要引入类用@class 替代。
  • 不允许定义不必要的属性或变量。
  • 不允许暴露过多的接口,原则上一般暴露一个依赖注入的接口供外部初始化。
  • 不允许存在多个@interface 声明,有且只有一个@interface。

**2、.m文件 **

  • 代码分区 #pragma mark - 代码分区说明 比如 "#pragma mark - 属性变量 #pragma mark - 界面布局"
  • 代码分区顺序 从上到下依次是 析构,初始化,布局,私有方法,公有方法,网络,回调,属性变量。
  • 所有的.m文件不得超过800行,超过请做适当逻辑分层。
  • 任意行代码不能超过80字符。(其实也很容易超过80字符,可以考虑多行显示,比如有多个参数时,可以每个参数放一行。)可以在Xcode中设置超过80个字符的提醒,选中“preference->TextEditing->page guide at column ”.设置完之后就会在代码80个字符处有一条竖线。
  • 代码折叠,这个可能是关于开发效率的,很有用。Xcode7默认没有开启代码折叠,如果你的方法体行数很长,看起来会很不方便,此时你就可以把方法“收起来”,一个类中的结构就会很清晰。开启方法如下:
    “Xcode菜单-->Preferences-->Text Editing-->勾选Code folding ribbon”.
  • 所有类必须实现dealloc函数。

**3、关于函数 **

  • 方法的声明与定义
  • 函数名要能清晰的说明函数的功能。
  • 参数名要能清晰的说明参数的含义。
  • 按照当前函数依赖关系,选择是否内部函数做参数检查。
  • 函数功能必须符合函数命名。
  • 函数参数不得超过6个,超过请考虑传参数包。
  • 所有函数不得超过150行。

**4、关于属性,变量 **

  • 在类内部声明实例变量在名字前以下划线“_”开头。比如_xxx。
  • 属性变量采用get方法做获取,俗称 “懒加载”。比如 存在属性变量 button.
 @property (strong,nonatomic) UIButton *button;
 #pragma mark - 属性变量 
 -(UIButton *)button
{
    if (nil == _button)
    {
        _button = [UIButton buttonWithType:UIButtonTypeCustom];
    }
    return _button;
}  

**5、语句规定 **

  • 合理缩进,排版清晰。
  • if 语句不得嵌套超过4层,超过请考虑设计是否合理。
  • 风险代码请加 warning注释。

**6、关于通知,基类,策略,宏 **

  • 任何新增一个通知,基类或者策略,宏都必须在团队内部讨论其必要性。

**7、工具类的整理 **

  • 不允许将各种方法笼统的用一个 common类来做,
    要做区分比如:涉及到字符串的可以放进 StringTools,颜色类的可以整理到 ColorTools 等。

**8、设计类遵照的原则 **

  • 职责单一。
  • 对外接口尽量追求简单好用,接口之间隔离。

**9、关于xib,storyboard **

  • 原则上不允许使用xib,storyboard,尽量使用纯代码。

**10、防御性的工作 **

  1. 对nil的检查
    • 仅在有业务逻辑需求时检查nil,而非为了防止崩溃。
    • 向nil发送消息不会导致系统崩溃,Objective-C运行时负责处理。

  2. BOOL陷阱
    Ojbective-C中定义BOOL为无符号字符型,这意味着BOOL类型可以有不同于YES(1)或者NO(0)的值。不要直接把整形转换成BOOL。常见的错误包括将数组的大小、指针值及位运算的结果直接转 换成BOOL,这取决于整型结果的最后一个字节,可能产生一个NO的值。当转换整形至BOOL时,使用三目操作符来返回YES或者NO。
    • 将int值转换为BOOL时应特别小心。避免直接和YES比较。
    • Objective-C中,BOOL被定义为unsigned char,这意味着除了 YES(1) 和NO(0)外它还可以是其他值。禁止将int直接转换(cast or convert)为BOOL。
    • 常见的错误包括:将数组的大小、指针值或位运算符的结果转换(cast or convert)为BOOL,因为该BOOL值的结果取决于整型值的最后一位。
    • 将整型值转换为BOOL的方法:使用三元运算符返回YES/NO,或使用位运算符(&&,||,!)BOOL、_Bool和bool之间的转换是安全的,但是BOOL和Boolean间的转换不是安全的,所以将Boolean看成整型值。
    • 禁止直接将BOOL和YES/NO比较。

    建议:

       BOOL great = [foo isGreat]; 
        if (great) 
              //TODO…
  禁止:
      BOOL great = [foo isGreat]; 
       if (great == YES)
          //TODO... 
  1. 属性
    • 严把权限:对不需要外部修改的属性使用readonly。
    • CFType使用@dynamic,禁止使用@synthesize。
    • 属性声明为nonatomic,除非你需要原子操作。

**11、关于 UserDefault **

  • 整个项目里面UserDefault 最多存储5个字段,并且相关存储字段应当说明注释在 UserDefault.h 文件里。
  • 任何添加UserDefault字段行为必须经过讨论决定可行性

12、注释

  • 注释原则:说明类职责、说明函数功能、说明变量、解释复杂或者特殊逻辑。
  • 所有属性 和 方法都必须添加注释。
    属性注释参考格式:
/*! @brief 传递照片的block **/
@property (nonatomic,copy) cameraImage photo;

方法注释参考格式:(同类别的多个方法要用 #pragma mark - 归类注释,方便查找)

#pragma mark - GET请求
/**
 *  @author 吴润源, 16-01-14 18:01:15
 *
 *  GET请求一般常用方法
 *
 *  @param urlStr     网络请求的url
 *  @param parameters 请求体参数
 *  @param success    请求成功回调
 *  @param failure    请求失败回调
 */
+(void)Get:(NSString *)urlStr parameters:(NSDictionary *)parameters success:(successBlock)success{
    
    //默认网络提示框为NetworkRequestGraceTimeTypeNormal    
    [self Get:urlStr parameters:parameters graceTimeType:NetworkRequestGraceTimeTypeNormal success:success failure:^(NSError *error, NSURLSessionDataTask *operation) {
               
    }];
}

三、代码优良习惯

1.判断nil或者YES/NO.
if (someObject) { ... } if (!someObject) { ... }
禁止:
if (someObject == YES) { ...} if (someObject != nil) { ...}
2.条件赋值
result = object ? : [self createObject];
禁止:
result = object ? object : [self createObject];
3.** BOOL赋值**
BOOL isAdult = age > 18;
禁止

BOOL isAdult;
if (age > 18)
{
    isAdult = YES;
}
else
{
    isAdult = NO;
}

4.** 拒绝死值**

if (car == Car.Nissan)
or
const int adultAge = 18;
 if (age > adultAge) { ... }

禁止:

if (carName == "Nissan")
or
if (age > 18) { ... }

5.** 复杂的条件判断**

if ([self canDeleteJob:job]) { ... }        

- (BOOL)canDeleteJob:(Job *)job
{
    BOOL invalidJobState = job.JobState == JobState.New || job.JobState == JobState.Submitted || job.JobState == JobState.Expired;
    BOOL invalidJob = job.JobTitle && job.JobTitle.length;
    return invalidJobState || invalidJob;
}

禁止:

if (job.JobState == JobState.New
    || job.JobState == JobState.Submitted
    || job.JobState == JobState.Expired
    || (job.JobTitle && job.JobTitle.length))
{
    //....
}

6.** 嵌套判断** (一旦发现某个条件不符合, 立即返回, 条理更清晰)

if (!user.UserName) return NO;
if (!user.Password) return NO;
if (!user.Email) return NO;
return YES;

禁止:

BOOL isValid = NO;
if (user.UserName)
{
    if (user.Password)
    {
        if (user.Email) isValid = YES;
    }
}
return isValid;

6.** 参数过多,传对象**
当发现实现某一功能需要传递的参数太多时, 就预示着你应该聚合成一个model类了

- (void)registerUser(User *user)
{
     // to do...
}

禁止:

- (void)registerUserName:(NSString *)userName
                password:(NSString *)password 
                   email:(NSString *)email
{
     // to do...
}

7.** 回调方法**
函数调用的可知性, 回调时被调用者要知道其调用者, 方便信息的传递, 所以建议在回调方法中第一个参数中加上调用者
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
8.** Block 的循环引用**
block中的self 要定义一个weakSelf 代替,用来避免循环引用问题。为了避免weak类型被释放,应在block内define 一个strong的self,进行非空判断。如果为空则抛出异常。如下:

__weak typeof(self) weakSelf = self;
myObj.myBlock =  ^{

    __strong typeof(self) strongSelf = weakSelf;
    if (strongSelf) {
      [strongSelf doSomething]; // strongSelf != nil
      // preemption, strongSelf still not nil
      [strongSelf doSomethingElse]; // strongSelf != nil
    }
    else {
        // Probably nothing...
        return;
    }
};

9.** Block规范**
在函数中使用到Block时, Block第一行与代码块必须得空行, 无论方法是否是系统自带的.
比如:

-  (void)someMethod {

    [className blockName:^(parameter) {

        // Code Body
    }];
}

禁止:

-  (void)someMethod {
    [className blockName:^(parameter) {
        // Code Body
    }];
}

10.** 其他习惯**

  • NSArray, NSDictionary成员的判空保护。在addObject或insertObject到NSArray或者NSDictionary时最好加一下判空保护,
  • Observer的移除。凡是注册的Observer,切记至少在dealloc里面移除一下observer.
  • commit代码之前一定要保证木有warning, 木有内存泄露, 确保都OK之后再上传代码. 其实很简单, 上传代码之前 Command + Shift + B 静态分析一下, 看看有木有什么issue.

四、代码样式规范

1.** 代码编写时候,添加空格或换行需注意:**

  • 二元运算符和参数之间要有一个空格,如赋值号=左右各留一个空格。
    self.myString = @"235423rew523452345";

  • 一元运算符和参数之间不放置空格,比如!非运算符,&按位与,|按位或。
    BOOL isClose = !isOpen;

  • 强制类型转换和参数之间不放置空格。
    NSString *str3 = (NSString*)self.myString;

  • 三目运算符和参数之间必须得有空格, 如判断符"?"结果符":"。比如:
    NSInteger userAge = @"Man" ? 18 : 19;
    禁止:
    NSInteger userAge = @"Man"?18:19;

  • 长的变量值应该拆分为多行。尤其体现在使用数组或者字典。以下也分别是快速声明数组@[]和字典@{}的方法。元素应对齐。样式参考:

NSDictionary *dict = @{@"name":@"jack",
@"age":@"20",
@"gender":@"female",
@"isMarried":@"false"
};


2.**代码推荐**

* 项目采用MVC框架,页面要按模块分类存放到对应的文件夹。

* 对传入参数的保护或者说是否为空的判断,尽量不要使用if(!obj),而使用NSAssert断言来处理。NSAssert是系统定义的宏。
`NSAssert(myName != nil, @"myName参数为空");`
   如果条件判断为真,则程序继续执行;
  如果判断条件为假,则抛出异常,异常内容为后面定义的字符串;

* 方法参数名前一般使用"an","the","new"来进行修饰。代表类方法和实例方法的"+"加号,"-"减号后需要一个空格。如:
`- (void)setPersonInfo:(NSString*)theID theName:(NSString*)theName theAge:(NSInteger*)theAge`

* 当需要一定条件才执行某项操作时,最下边的应该是最重要的代码,不要将最重要的代码内嵌到if中。
如良好的风格是:

  • (void) someMethod {
    if(![someOther boolValue]) {
    return;
    }
    //最重要的代码写在这里;
    }
禁止:

  • (void) someMethod {
    if([someOther boolValue]) {
    //重要代码;
    }
    }

* UIView的子类初始化的时候,不要进行任何的布局操作。布局操作应该在layoutSubviews里面做;需要重新布局的时候调用setNeedsLayout,而不要直接调用layoutSubviews

* 界面布局建议使用第三方控件masonry。代码简洁,逻辑清晰。


3.**代码对齐规范 **

* 任意行代码不能超过80个字符, 如果超过80个字符, 可以考虑多行显示, 比如有多个参数时, 可以每个参数放一行.
比如:
  • (void)replaceObjectsInRange:(NSRange)range
    withObjectsFromArray:(NSArray *)otherArray
    range:(NSRange)otherRange;

* 在每个方法定义前需要留白一行, 也就是每个方法之间留空一行, 如果是系统的方法则可以不留空.

比如:
  • (void)viewDidLoad {
    [super viewDidLoad];

    // Code Body

}

禁止:
  • (void)someMethod {}

    [self someMethod];

}


* 一个实例变量出现属性赋值时, 如果是以“=”赋值, 必须以“=”对齐, 如果以“[]”赋值, 必须以开头第一个“[”对齐.
比如:
  • (void)setTextFieldDelegate:(id)textFieldDelegate {

    self.phoneTextFieldOne.delegate = textFieldDelegate;
    self.phoneTextFieldTwo.delegate = textFieldDelegate;
    self.phoneTextFieldThree.delegate = textFieldDelegate;
    self.reviewPassword.delegate = textFieldDelegate;
    self.confirmPassword.delegate = textFieldDelegate;
    }

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

推荐阅读更多精彩内容

  • 本规范原文来自Github.但是markdown格式有问题,我重新排了下。实际开发中根据具体情况决定如何取舍,但是...
    LeonXtp阅读 464评论 0 0
  • 一、命名规范 1.类的命名 1.1 大驼峰式命名:每个单词的首字母都采用大写字母 例子:M...
    NB_killer阅读 1,617评论 0 1
  • iOS开发规范 引子 在看下面之前,大家自我检测一下自己写的代码是否规范,代码风格是否过于迥异阅读困难?可以相互阅...
    BGDane阅读 1,360评论 1 4
  • iOS开发规范 目录 编写目的 制定开发规范,可以在团队内部形成统一的开发习惯,减少协作的理解成本。此开发规范主要...
    Lorne_coder阅读 578评论 0 4
  • 语言 使用US英语 代码组织 在函数分组和protocol/delegate实现中使用#pragma mark -...
    andyJi阅读 197评论 0 0