iOS代码规范和优化建议

公司需要写一个代码规范,本人第一次写,完成后记录在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来包含起来及时释放。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值