编码规范和优化建议
公司需要写一个代码规范,本人第一次写,完成后记录在blog
一、命名规范
1、类定义
例: ThisIsAClass,
采用苹果推荐的方式,首字母大写,多个有实际意义的英文单词组成,每个单词的首字母大写。
在此基础上类名需要体现出这个类的类型
试图控制器:ThisIsAViewController
试图:ThisIsAView
按钮:ThisIsAButton
等等
个人不建议使用缩写,因为代码补全非常完善,单词再长也不会影响方便性,但缩写影响可读性
可讨论决定是否可使用类似 ThisIsAVC,ThisABtn,之类的缩写方式
2、属性
例:thisIsAObject
首字母小写,多个有实际意义的英文单词组成,每个单词的首字母大写。
在此基础上,需要提示这个变量的类型或者用途。
例如:
UITextField * userNameTextField;
UIButton * leftTopButton,doSomeActionButton;
等等
缩写问题同上,可讨论。
3、成员变量
_menberObject
在属性的基础上,需要以下划线开头。
其他规则和属性一样。
4、局部变量
参照属性
5、函数命名
首字母小写,多个有意义的单词组成,:后面首字母小写
例:
- (void)loginButtonDidPressed;
- (void)loginWithUserName:(NSString) userName password:(NSString *) password;
等等
6、函数形参
参照属性
若有可能与属性命名相同或者冲突,建议加上in,a等前缀
例:
- (void) someMethod:(id) inValue anotherObject:(id) aObject
{
_vlaue = inVlaue;
self.object = aObject;
}
7 、前缀
由于oc没有命名空间的概念,所有苹果建议使用前缀来防止第三方库、子工程之间命名冲突。
目前我们的工程中有的没有前缀,有前缀的又不统一,需要讨论是否统一。
二、代码组织
1、概述
目前我们的工程都采用MVC构架
所以页面代码都至少要包含,View(特别简单的可能省略),VewController(UIVewController子类),ModelManager(这么命名是区分于瘦model,也就是entity)
三者之间最好通过接口互相访问,隐藏实现细节。
比如,VC不需要知道view有哪些label,textfileld,只需要view提供加载数据的方法就可以,实现松耦合。这样如果只是页面排版变了,只要修改接口实现就可以,不用修改其他部分。这也是swift发布后,苹果提出的面相接口(协议)编程的思想。
规则提出的目的,都是指导我们对各模块之间职能划分,使各自功能单一,松散耦合,任一一方修改,尽可能的不影响其他模块。
2、页面
UIView:只做显示逻辑不做业务逻辑,它应该是纯被动的接受VC提供的数据,进行展示。
对用户操作进行收集,提示VC事件发生。
下面提供一些开发场景的处理建议:
case 1
如果view包含一个imageview,性别变量sex,值为male,显示图a,sex为female,显示图b,这种逻辑应该在VC中实现,而不应该把sex传给给View去保存。
//VC中
- (void) someMethod
{
if (self.sex == male) //假设male为枚举
{
[view loadImage:imageA];
}
else
{
[view loadImage:imageB];
}
}
//view中
(void)loadImage:(UIImage *)inImage
{
self.imageView.image = inImage;
}
case 2
不要在init方法中,去取自身的frame,并以此对子view设置frame,而应该在layoutSubviews中设置
因为很多时候,init方法中,view本身的frame并不是最后显示的frame
- (void)layoutSubviews
{
// 一定要调用super的方法
[super layoutSubviews];
//子view的frame设置
…
}
case 3
只是堆砌子View的情况一般不太需要自定义view。
自定义view,其实更应该发生在需要重载drawRect方法,或者事情响应touchesBegan/touchesEnded等方法时。而大部分情况其实不需要重载这些方法。只需要addSubvew。
这种情况其实更应该考虑是建一个子视图控制器,毕竟管理一个视图层级(view hierarchy)是VC的事情。
3、视图控制器
试图控制器负责:从并且只从modelManager获取数据,展示到View上。并且接受modelManager数据变化发生时通知,提供回调方法。
开发场景:
case 1
loadView:一般不建议重载,实在要用根据文档所诉,只应该创建视图层级(view hierarchy)也就是只应该做alloc和addSubview。并且如果重载它了,不要调用super,额外的赋值需在viewDidload之后再做。
注意:如果使用了xib或者storyboad就不要重载这个函数。而应该使用awakeFromNib。
case 2
viewDidload:作为入口,希望它能比较简洁直观,所以建议它内部只出现toDoList型的函数,具体的实现到各个函数中实现。
例如:
- (void) viewDidLoad
{
[super viewDidLoad];
[self setupViews];
[self registerNotifications];
[self setupModelManager];
[self doOtherThing];
}
建议在viewDidload中使用自动布局来管理View及子view的frame
case 3
viewwill/DidlayoutSubviews:这对函数在 view调用layoutSubviews前后调用。如果你没用使用自动布局,应该在这两个函数中对view进行位置的设置。
case 4
关于展示用的数据,除非必要,不建议用额外的属性/成员变量保存。而直接通过modelManager的函数获得。以便由modelManager控制数据保存的位置是内存变量,还是持久化方式。
实在需要在内部函数间传递,也尽量根据最小知道原则,保存索引,状态值之类的数据。
4、modelManager
modelmananger负责从一个或多个数据源,采集数据业务操作后提供给VC,数据来源可能是数据库,内存缓存,网络等中的一个或多个。
开发场景
case 1
对VC隐藏数据源的原始数据结构。
也就是说尽量不要把数据源原始数据直接交给VC,而应该与VC约定好相互之间需要传递的最小单元(实体/瘦model),把从一个或多个数据源获取的原始数据,转化成该最小单元。提供给VC
case 2
与VC交互的协议接口定义,应足够独立,一个事件/一次数据请求,通常不应该需要调用到其他接口(这里不是说不能调其他内部函数,而是对VC 开放的其他接口)
case 3
接口实现优先进行内存操作,内存没有的数据再操作数据库,还没有再进行网络操作。实现多级缓存及时反馈。
case 4
页面之间的数据变更响应,应该在modelManager之间通知和响应
具体来说:
vc1某操作,导致vc1的modelManager有数据变更,这个变更影响到v2,那么不应该由v2去接受这个变更通知,而应该由vc2的modelManager接受并修改自身的数据,通过自己的数据变更接口通知vc2。
也就是说一个VC只接受一个modelManager的数据更新通知。
case 5
尽管上面的建议一个VC只对应一个modelManager。但有些场景下,多个VC可以共享一个modelManager。
例:
设置功能下有多个子设置项,这些设置项是不同子页面操作去设置的并且很简单,当我们把设置项的数据作为一个整体去考虑的时候,这些子设置项操作的其实应该是同一个数据对象。此时应该支持这些页面传递modelManager对象来共享它。
5、其他衍生组件
在MVC的框架下,可根据功能单一原则,拆分出多个功能组件,供三大框架模块使用,比如数据库模块,网络模块,加密模块,播放器模块等等。
三、代码优化建议
case 1
任何情况下总是使用setValue:forKey代替setObject:forKey
因为前者在value非nil情况下等于后者。
在value为nil删除key所对应的键值。由于赋值时一般当前并不会存在key的键值,即使是覆盖某对键值,在值为空的情况下,删除了它也是合理的。
所以前者效果完全等效于检测value非空后调用后者。
后者object为nil则直接crash。
所以使用setValue:forKey更安全。
case 2
隐藏不必要的接口和属性。
那些只在内部使用的属性和函数,应定义在类扩展之中,把少数愿意暴露出去的函数和属性定义在头文件中。
case 3
尽量不要使用成员变量,用类扩展中的属性代替它。然后总是使用self.去访问它。
这样有更清晰的内存管理。
并且,当有一天你突然需要在赋值或者取值的时候做一些事情的时候,你就只需要在set/get中操作。而不用整个类里面到处修改了。
case 4
尽量不要阻塞主线程,尽量把非UI操作放到其他线程执行,完成后回到主线程通知UI刷新。
case 5
NSSet/NSArray,他们的区别是,NSSet是无序的,当一个数据集合不关注排序时,使用NSSet,查找元素是否存在和移除效率是高于NSArray的。
例:
维护一个操作中的数据集合,操作开始后加到集合中,操作完成后移除出集合,这种情况下应当使用NSSet
case 6
无论什么时候通过索引访问数组都应该进行越界判断。或者try catch
case 7
懒加载,对不常用的View,不要在页面打开时就创建,采用懒加载的方式去实现。以减少页面启动时间。
case 8
预加载,对于加载速度比较慢,又需要进入页面就直接显示的数据,考虑在适当的时间提前加载好,驻留在内存中。空间换时间。
case 9
tableView的使用除了注意cell正确重用以外,还需注意,数据源的count变化,特别是变小是要及时reloadData。如果使用insertRow/removerow操作,一定要保证数据源数量等于变化前数量加/减变化数量,否则就会crash
case 10
实体中只能进行简单的数据操作,例如json转字典,数值转字符,取整之类的。不能进行业务操作,如存取数据库,网络操作等。
case 11
使用大量临时变量时,可用@autoreleasepool来包含起来及时释放。