iOS代码规范

前言

最近突发奇想,想到之前公司培训的代码规范,所以简单的整理一下,希望能对大家有所帮助。

正文

部分一

1. 命名
  • 一定要含义明确,避免产生歧义,避免不必要的缩写,一般规则为类名,接口名(Protocol)均以大写字母开头,多单词组合时,后面的单词首写字母大写。

  • 成员变量以小写字母开头,多单词组合时,后面的单词首写字母大写。

  • 成员变量使用名词(表示某个属性,如title,bookid等)或形容词(表示状态,如enabled,hidden等)

  • 函数、方法使用动宾格式(表示一个功能,如getTitle,parseXmlData,calculateScore等)。按照apple的惯例,私有成员变量以下划线(_)开头。
    另外,像 onPlayOrCollectOrClock 这样的函数名以后应该被严格禁止!!!

  • 一种解决方案是:
    打包在一个函数中处理,根据类型匹配区分操作。

2. 内存管理

1)注意delegate用weak类型
2)用到block的地方一定要注意block块里边的对象是否出现循环引用的问题(一般根据dealloc是否正常执行可以判断)

3. 关于魔数
  • 原则上禁止在代码中硬编码(hard coding)使用魔数(magic number)。比如下面的代码应该被禁止
    if(booktype == 1) {
    } else if(booktype == 2) {
    } else if(booktype == 3) {
    }

这种情况应该在某个集中的位置或通用头文件中定义一组宏或枚举值来表示不同的类型,如

typedef NS_ENUM(NSInteger, BookTypeEnum) {
    BookType_Computer,
    BookType_Education,
    BookType_Comic
};

另外, 不仅仅硬编码的数字被称为魔数;硬编码的常量字符串也是魔数!如果两个以上(含)地方使用到同一个魔数(比如:if([nssting isEqualToString:@"methodType"])),就要在一个统一的地方以合适的名称定义一个常量或宏来表示。

4. 调试/测试

1)要善于NSAssert、assert等工具(在debug模式有效)。合理安排断言,也是对自身对业务逻辑理解程度的一个检验。(使用方式:NSAssert([curList count] > 0, @"Empty catalog list got!");//第一个参数如果不符合条件(默认应该符合),则输出第二个参数(一般为异常情况))
2) 决不要仅满足于完成代码。完成一个功能要自己完整走一下流程,在第一时间消除不必要的bug。

5. 代码管理和版本控制

1)对于未确定处理逻辑或暂时的测试代码。统一加上在上一行加上注释“///TO DO:<有待确定的内容>“,提交代码时全project搜索”TO DO:"确认。
2) 提交代码时,在commit确认框中,养成输入说明信息的习惯。说明内容要明确表述所做修改。
3) 代码要及时上传。

6.关于基类的使用
  • 有很多功能类似的类可以抽取出共同部分,继承一个自定义基类,基类里抽象出共同的属性和函数方法,在子类里统一调用基类的属性和函数,需要强调的是调用基类的方法时,注意子类回调。
7.关于子/父视图的处理
  • 学习和习惯用面向对象和封装的思维方式来思考问题,不管是代码加载界面还是使用xib文件,界面的控件一般分上中下三部分作区块化处理,分别用一个底view(superview)来承载,当系统的自动调整尺寸,或者删除某一部分视图时,只操作底view即可,避免到处手动对一堆子view调整、删除。
8.关于代码结构的组织。

随着代码量的增多,代码结构的合理组织显得尤为重要。
1)建议将各个函数按功能划分区块,并用“#pragma mark -”隔开,这样可以借助XCode方便的定位代码。一般格式如下:

#pragma mark -
#pragma mark 功能区块的详细描述

2)对于category,函数的声明和定义位置一定要匹配!消除不必要的warning。
例如,在“@interface MyClass(MyExtend)"中声明的方法,一定要在“@implementation MyClass(MyExtend)”中定义,而不是在“@ implementation MyClass”中。

9.关于项目文件的组织结构。
  • 为了方便团队协作,工程中引用的库和外部文件,一律使用相对路径,并且本地的目录结构务必保持与SVN(CVS)服务器上的目录结构一致。

部分二

1. NSFileManager在多线程中的使用。
  • 根据apple docs的建议,在多线程中,主线程以外的其它线程不要直接使用

[NSFileManager defaultManager]
而是alloc一个新的实例:
NSFileManager *fileMan = [[NSFileManager alloc] init];
//other code
[fileMan release];

2. 图片资源格式问题。
  • 根据以往经验,有些同学或者UI部门提供的PNG图片其实是伪装的(直接将jpg图像后缀改为png),此种情况下,可能会导致在build device版本时无法成功copy到app target中,造成显示不出来。这一个一定要注意!
3、需求文档、效果图和设计文档。
  • 需求文档一定要认真阅读,确保准确理解需求。

  • 明白区分“技术实现”和“产品实现”的区别;对开发进度评估不要故意延长时间,也要注意不要盲目乐观。

  • 产品开发启动的前提是需求文档、设计文档和效果图全部提供,缺一不可,否则原则上拒绝给出具体的开发时间表。

  • 开发过程中要严格按照效果图实现,有问题的及时找产品经理和UE设计人员沟通。

4.关于静态库(static library)。

1)静态库中的对SDK Class的category不被link到app的问题,在 Other Link Flags中添加“ -ObjC”。
2)静态库中对自定义Class的category不被link到app的问题,在Other Link Flags中添加“ -all_load”。

部分三 工程目录结构

  • 主目录:放的是AppDelegate和一些系统常量及系统配置文件;

  • Base:一些基本父类,包括父ViewController和一些公用顶层自定义父类,其他模块的类一般都继承自这里的一些类;

  • Controller:系统控制层,放置ViewController,均继承于Group Base中的 BaseViewController或BaseTableViewController;

  • View:系统中视图层,由于我比较喜欢通过代码实现界面,所以这里放的都是继承于UIView的视图,我将视图从ViewController中分离出来全部放在这里,这样能保持ViewController的精简;

  • Model:系统中的实体,通过类来描述系统中的一些角色和业务,同时包含对应这些角色和业务的处理逻辑;

  • Network:网络请求,每个请求以API为前缀命名,对应上相应的Model

  • Manager:管理类,如数据缓存、推送设置、分享类、定位类等(常将第三方类库整合成自己的api来使用)

  • Utils:系统工具类(AppUtils),主要放置一些系统常用工具类;

  • Categories:类别,对现有系统类和自定义类的扩展;

  • Libs:用到的第三方类库

  • Resource:资源库,包括图片,plist文件等;

保持ViewController简单
1、View视图与ViewController分离
2、业务逻辑与ViewController分离

部分四 实践

  • 不要使用尤达表达式
    //尤达是星球大战中的jidi武师,他说话的语法都是很奇怪的倒装句,这里泛指这样的代码

if ([@8 isEqual:a]) // 错误
if ([a isEqual:@8]) // 正确

  • 不要和YES,NO,Nil这些值做比较

if (great == YES) //错误
if (great) //正确

  • 不过过度使用if嵌套,有时可以用return减少if嵌套
- (void)someMethod {
  if (![someOther boolValue]) {
      return;
  }
  //Do something important
}
  • 复杂的表达式应该拆分多个语义更明确的字句

  • 三元运算符 ? 应该只用在它能让代码更加清楚的地方

  • 注意stong,weak,copy,assign的正确使用

  • 尽量避免使用c的基本类型 ,例如:

int -> NSInteger
unsigned -> NSUInteger
float -> CGFloat
动画时间 -> NSTimeInterval
enum -> NS_ENUM

  • 工厂方法或类构造器方法,返回类型需要使用instancetype而不要用id类型,方法中需要使用 [[xxx class]alloc] init]方法构造对象而不要用[[XXXClass alloc]init]

  • 永远不要在init dealloc方法中使用 getter,setter,.属性等,你应当直接访问实例变量

  • 类的构成方法(init方法)需要遵守Designated Secondary原则。这点在swift中被强制规定init和convenience构造方法。
    designated 初始化方法一般提供所有的参数,有且只有一个,而secondary 初始化方法是一个或多个,并且提供一个或者更多的默认参数来调用 designated初始化方法

  • 定义你的 designated initializer,确保调用了直接超类的 designated initializer。

  • 重载直接超类的 designated initializer。调用你的新的 designated initializer。

  • 为新的 designated initializer 写文档。

  • 永远不从 designated initializer 里面调用一个 secondary initializer

  • 注意初始化模式的合理使用(类簇,单例)

  • 合理使用懒加载Lazy loading
    当实例化一个对象需要耗费很多资源,或者配置一次就要调用很多配置相关的方法而你又不想弄乱这些方法时,我们需要重写 getter 方法以延迟实例化

  • 要重写类的相等性时,需要同时实现isEqual和hash方法

  • 常量推荐静态常量的声明,如果要暴露给外部,使用extern关键词,例如:

extern NSString *const ZOCCacheControllerDidClearCacheNotification;
static NSString * const ZOCCacheControllerDidClearCacheNotification = @"ZOCCacheControllerDidClearCacheNotification";

  • 定义你自己的 NSNotification 的时候你应该把你的通知的名字定义为一个字符串常量

// Foo.h
extern NSString * const ZOCFooDidBecomeBarNotification
// Foo.m
NSString * const ZOCFooDidBecomeBarNotification = @"ZOCFooDidBecomeBarNotification";

  • objc中可以合理使用代码块的特性,使局部变量更清晰
    //代码块如果在闭合的圆括号内的话,会返回最后语句的值

NSURL *url = ({
NSString *urlString = [NSString stringWithFormat:@"%@/%@", baseURLString, endpoint];
[NSURL URLWithString:urlString];
});

  • 合理使用Pragma Mark区分:不同功能组的方法,protocols 的实现,对父类方法的重写,View 的生命周期,自定义访问器,

  • 方法注释最好用/** 开头,换行后第一句写方法的一句话描述,空一行后在写余下的描述, 还可以使用 | 来引用注释中的变量名及符号名而不是使用引号,例如 // Sometimes we need |count| to be less than zero.

  • 函数定义中的block参数应该尽量放到最后一个参数,把需要提供的数据和错误信息整合到一个单独 block 中,比分别提供成功和失败的 block 要好

  • (void)downloadObjectsAtPath:(NSString *)path
    completion:(void(^)(NSArray *objects, NSError *error))completion;
  • 函数定义中的error参数,尽可能方法函数的最后一个函数,并且遵循,数据为空,那么error不为nil,和error不为空,数据必须为空

  • block合理的使用self,weakself,strongSelf
    一般来说:如果block不是属性则使用self,是属性但block中调用单个self的方法时用weakSelf,多个方法用strongSelf

  • block如果一行可以写完块,则没必要换行,块内的代码须按 4 空格缩进,果块太长,比如超过 20 行,建议把它定义成一个局部变量,然后再使用该变量,

  • 不要使用new方法创建对象


结束语

到这里就结束了,如若不懂的话可以👇留言,也可以加入群讨论
喜欢的话 记得关注、收藏、点赞哟

群号:552048526

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

推荐阅读更多精彩内容

  • 禅与 Objective-C 编程艺术 (Zen and the Art of the Objective-C C...
    GrayLand阅读 1,607评论 1 10
  • iOS编程规范0规范 0.1前言 为􏰀高产品代码质量,指导广大软件开发人员编写出简洁、可维护、可靠、可 测试、高效...
    iOS行者阅读 4,446评论 21 35
  • 概要 Objective-C是一门面向对象的动态编程语言,主要用于编写iOS和Mac应用程序。关于Objectiv...
    DreamMmMmM阅读 1,159评论 0 7
  • “老龙头”是我读本科时的古代文学老师,他才华横溢,但是性格却十分豁达古怪。因此被称为“学院四怪”之一。 他50多岁...
    遇见小晴天阅读 211评论 2 1
  • 明天就到节假了,心情带着些许雀跃。正在做工作规则,没承想被隔壁办公室稀稀拉拉的争执声给打断了。 细听,才知道是法务...
    亦若莎阅读 582评论 0 3