您的位置:首页 > 移动开发 > IOS开发

我的iOS一年半经验

2015-11-01 11:34 232 查看
从大二开始我的iOS之旅,到现在已经将近两年了,还是一事无成,虽然在现在能拿到一些offer,但是都不是自己心仪的,能留下的也就是很多遗憾。 之后加入了一个牛人聚集的iOS流浪群,里面有百度iOS leader,有滴滴iOS leader等,非常感谢有这么一个高级圈子,群主彭哥也帮助我许多,技术上提升了一大步,在此记录一下学到的一些经验。

一. 减少对象的属性

1. 头文件中不要暴露太多的东西;

2. 使用extension定义不暴露属性,并且可以申明我们要遵循的协议

3. 使用category将方法定义到这里面,属性不要定义(因为不会生成对应的实例变量)

4. 不要添加一个多余的属性,不留注释掉的代码,不留没有用的代码,但是很多开发者就是做不到或者说对代码没有爱

5. 关于使用__block,这个是android开发中最不满意的地方,这个特性简直太他妈爽了,即 :使用block的时候回传一些参数

6. 有时候可以使用__block变量代替全局静态变量

7. 使用block的时候,有时候会用到对象的_xxx变量,这时候会对self retain一次,就会造成循环应用,如果使用局部变量,就将问题扼杀在摇篮中

二.减少对象消息

减少UI的action类消息,使用block或者RAC,让UI和action的代码终于在一起

在日常开发中,把自己的protocol和target/selector尽量使用block开代替,相信我,这样会使代码好读许多’

三. 模块化每个对象的代码

使用@pragma mark -xxx是来分割不同逻辑代码之间的界限,使用Ctrl + 6 来查看代码目录结构,尽量避免文档结构超过两个屏幕,超过两个屏幕说明代码有点多了, 个人习惯将模块分为 Life cycle , UI helper , network , dataSource \ delegate等等

#pragma mark - Life cycle
生命周期,类似addSubview和Notification的监听和销毁都放在这里

#pragma mark - Interface
接口

#pragma mark - Event response

#pragma mark - Private method
如果是ViewController,这个地方就是瘦身的关键,业务和逻辑功能相关的就放到ViewModel里。

#pragma mark - Delegate
代理

#pragma mark - Getters and Setters
建议所有的Property都设置,这样修改配置会比较方便,看起来不会很混乱


四. UI开发

1. 重写setter, getter方法和Code Block Evaluation C Extension语法

重写UI的getter方法,把UI的初始化放在getter中,减轻 -viewDidLoad的负荷,同时可以使整个页面变得清晰;同时,可以通过使用使用GCC Code Block Evaluation C Extension ({…})语法,结构化地初始一些UI控件。关于setter代码风格,可以参考别人写的一篇文章,http://casatwy.com/iosying-yong-jia-gou-tan-viewceng-de-zu-zhi-he-diao-yong-fang-an.html,这个问题之前在我们Q群里探讨之后我也非常认同这种方式写UI。

举一个例子,-viewDidLoad中,做为逻辑的入口,代码会变少但是变清晰,代码如下:



然后重写bgView的getter方法,包括View和frame这些都可以使用({…})语法使代码结构化层次化:



2.View创建方法:

在解决炫酷的UI的时候, 我的解决办法是 : 组合式UI, Custom view, container ViewController

(1)组合式UI

对于一个复杂的UI,直接把所有的subviews直接堆积到super view上面,这样的结果就是,调整subview的frame非常困难。我个人的做法是,首先对复杂UI进行分块,从左到右或者从上倒下,把各个UI元素放到不同的container view上面,然后组合这些container view放到super view上面,这样的好处非常明显,首先UI干净清晰,阅读起来不那么费劲。其次就是你计算坐标或者设置约束会变得很简单,因为你调整一个UI元素的时候,只需要考虑它与包含它的container view的坐标关系即可,而不是通过一大堆无趣计算跟最外层super view关联起来。还有就是可以充分利用Auto Layout和autoresiziingmask这些UI利器,使用的时候会非常方便。再有就是结合RACObserver这个利器之后,你能很容易做到根据data来update ui。

举个例子,是我们项目中前一段时间我重构的一个页面,这个首页列表,性能要求比较高。并没有使用Auto Layout来实现,但是不使用Auto Layout并不是不把它写的很干净的理由。

这是我对一个UITableViewCell的分层,最外层由 icon view / right view / bottom view这些container view组成,而right view这个container view则又是由right top view / right middle view /right bottom view 这些 sub container view组合而成,而具体的UI元素则是放在这些sub container view之中。这样UI代码就会以一种层次化样式展示出来,init/layoutsubviews只需要维护self与container view的关系即可,而具体展示数据的UI元素也只跟sub container view存在坐标关系。

关于性能的话,感谢iOS,我们不存在Android中页面层次较深性能卡顿的问题,放心把UI层次化就行

(2) custom view

对于非常复杂并且相对独立或者可以重用的UI,及时使用custom view子类化。对于单纯的展示UI,我们只需要简单通过组合式view就可以实现了。但是有时候,我们会遇到一些包含无论是动画,逻辑都比较复杂的情况,这个时候使用组合式View去实现,一方面容易把逻辑弄混乱,会把文件的文档结构变得很复杂,简单来说就是对象的消息数量很多。这个时候,我们可以通过custom view来实现,实际上这个也是组合式view,但是我们是把这些组合式view变成了一个类而已,只暴露少量的接口给外部调用。如果这个custom view会出现在多个业务模块中,那么有必要使用一个单独的文件来容纳这个类,如果仅仅是这个模块一个使用的话,可以直接写在这个业务模块的文件中即可,没有必要对所有的类都单独一个文件,我们就当作这个“内部类”来弄了。

什么时候使用custom view而不是组合view,我想了很久,你觉得组合式view的代码很乱的时候,别客气,包装为一个custom view就行了。我这边最近遇到的几个问题是使用UICollectionView来做部分UI的时候,同时还有其他很多UI元素,我会写一个custom view。比如下面这个文件,把一个左右滑动查看图片的UI使用PhotoView这个custom view进行包装,内部使用UICollectionView实现一部分相对独立的模块,这个时候这个控件实际上是可以包装为一个相对独立的模块的,用子类我感觉比较合适一些。

(3) container view controller

这个用法很多开发者不熟悉或者说是用的不多,但实际业务中,这个技术非常有用途,可以大大提高开发效率。对于有相对独立业务逻辑以及生命周期要求的业务,使用child view controller进行包装,如果parent view contrller与child view controller之间非常密切,则使用View Model以及block来对parent view controller和 child view controller 进行衔接。

使用child view controller来开发UI而不是custom view的优势很多,我个人认为最大优势在于可以方便利用View Controller的生命周期以及View Controller Hierarchy,比如在-viewWillAppear/-viewDidDisappear中做一些操作,再比如直接获取UINavigationController指针等等。之前的做法一般是在View Controller的对应生命周期内调用custom view的方法,传递self.navigationController指针给custom view等。所以可以不仅仅把UI相关的代码包装进入这个child view controller,也可以把网络请求,数据处理这些这些逻辑放到child view controller中,这样下来就能避免那种动不动超过1k行的view controller的出现了。

利用MVVM之后,还有一个比较有好处的用法,比如公用一些数据的时候,之前我们是把对象传递来传递去,这样的问题是很容易出现混乱,这个时候我们是传递ViewModel就可以避免这个问题,ViewModel既负责网络请求又负责数据处理,而parent view controller与child view controller所需要做的事情就是跟ViewModel进行binding而已。

五. Auto Layout/Masonry

在一些性能要求不是那么强烈的非列表页,我们可以大量使用Auto Layout来开发UI,充分利用UI根据数据的自适应能力,连在container view中调整UI的步骤都不需要了。之前有一段时间我根本不想开发iOS,原因很简单,Android的布局式以及可见式的开发方式非常方便,再加上AS这样的神器,我自己感觉效率不比iOS低。自从项目最低支持变到iOS6之后,我才开始使用Auto Layout,虽然比较费劲,但是感觉这个对UI开发来说是个解脱。

至于Masonry这个框架,之前我对这个抱有一定的怀疑不敢使用,所以我把源码读了一遍,发现这个包装很薄很巧妙,很多设计思路也值得借鉴。读完源码之后,尝试着完全使用Mansory来开发一个展示信息的页面,感觉太爽了!

这个的优势就是你设置UI的数据之后,不需要再考虑去update ui了,这样世界瞬时就清净了。。。。,下面是我一个简单的示例,结合({….})语法和RAC,可以使用最简单的label这样的命名来对UI设置数据,这个对我们开发UI来说,绝对是一种解脱。





六.说说Auto Layout的问题

view设置为hidden时 约束还在

UIView如果hidden的话,它的约束仍然是work的,所以会留下空白,不会像Android中那样设置GONE那么方便。国内sunny大神开源一个不错的解决方式,https://github.com/forkingdog/UIView-FDCollapsibleConstraints

动画问题

使用Auto Layout有一个比较大的问题在于动画,通过更改约束来进行动画,一直是我比较头疼的问题,所以一般遇到这类问题的时候,我都会尽量避免使用Auto Layout来解决,而是使用frame的方式来做。可以参考objc.io上面的一篇文章:http://www.objc.io/issues/3-views/advanced-auto-layout-toolbox/

多行UILable问题

iOS7以及以下的操作系统上,UILabel显示多行文本是又不足的,你需要设置UILabel的preferredMaxLayoutWidth为一个固定值才能显示多行文本。在iOS8以后就不再需要设置这个了。



UIScrollView的问题以及约束歧义和其他问题

参考文章:/article/2607033.html

七.注释

不要写一堆中文注释,代码不要出现大量的中文,OC已经够啰嗦,不要这么啰嗦地写码。除了提供服务的public功能或者方法,业务代码仅在某些关键点上注释一下就行,不需要一大堆中文,这样太low,代码自注释即可,需要注释的,可以通过喵神的Xcode插件来实现,https://github.com/onevcat/VVDocumenter-Xcode

八.善用OC的语法糖以及新语法

OC有很多新的语法糖,可以大大提高我们的效率,参考Apple Guide:https://developer.apple.com/library/ios/releasenotes/ObjectiveC/ModernizationObjC/AdoptingModernObjective-C/AdoptingModernObjective-C.html

还有一点是多用字面量语法,有一本书叫可以推荐看看 《编写Objective-C的52个高效方法》

九.JSON数据的处理

新手往往会被这个稍微困惑一下,比如服务器返回的数据格式不正确啦,包含null啦,都很容易引起项目崩溃。这个问题可以使用Mantle来解决,很多兄弟都在使用这个,我自己倒是一直没有用过。github上面有一个比较好用的框架可以看看

https://github.com/lihei12345/CYJSONValidator,这个在我们项目内部也在使用,效果不错,用来解析数据的时候,对数据的类型以及是否为null等进行校验,确保解析出来数据类型的正确性。对于可能不存在key的时候,还可以设置一些默认值。

十.block

使用block代替delegate,这个没啥可多说的,把代码变得非常紧凑,减少文件的消息数量,最主要的是关系没那么紧密了。对于有大量的delegate方法才考虑使用protocol实现,这个时候block太多也影响阅读。

同时,对于传递target/selector,也尽量使用block吧,这种阅读查找起来太不方便了。

十一.RAC封装网络请求



使用RACCommand封装请求,查看这几篇文章:http://codeblog.shape.dk/blog/2013/12/05/reactivecocoa-essentials-understanding-and-using-raccommand/https://github.com/ReactiveCocoa/ReactiveCocoa/issues/963https://github.com/ReactiveCocoa/ReactiveCocoa/issues/1326

结合RACCommand和takeUntil:来封装一个可以cancel的请求。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: