杂谈
突然想写点啥,不那么正式,就当杂谈一样看吧,说说从某些角度来看objc客户端开发。(objc的牛人看到有什么不对的就直接指出,也算给一种学习的机会)
闪退:
空对象放到了Dictionary里面,对象回收后被访问,Target没有对应的SEL,界面操作没有用mainThread……,对客户的直观体现就是闪退,有人提到客户端闪退就一脸的鄙夷,觉得程序员对异常都没怎么很好的处理,其实客户端比服务端的不可控因素要多的多,所以用服务端的角度去评价客户端或者去try catch来处理客户端的分支边界所有的异常,基本是不靠谱的,而且我记得写服务端的有一句常理:不要让系统带病运行。客户端也是如此,分支异常闪退有时候是种保护,因为客户端影响的是一个客户,重启的代价要远低于让用户杀死有问题的进程。(当然以上说的闪退在常规流程频繁发生,那就直接被卸载了,因此做objc底层的人还是要用服务端的细心来保护客户端的体验)
空指针:
当年Java刚出来的时候,最大的卖点就是:没有指针的复杂度,的确gc是语言的一种很大的进步,看看今天的java虚拟机已经超越java语言本身就可以知道它的价值所在,objc从早期的autorelease,到5的ARC其实都在帮助开发者来忽略指针的问题,其实关键还是在于对象传递,当系统越大越复杂的时候,开发者就越难把控对象在系统中的生命周期。objc中的空指针带来的危害分成两种:1.脏对象。(相当于开门走出去以后,才发现门背后是悬崖)。2.无反应带来的各种诡异。(java中如果操作一个空对象立马就空指针错误,而objc中对一个空对象做任何method调用,结果就是什么都没发生,同时如果你还准备操作后返回结果,那么该结果会正常的得到nil,这种坑会让很多程序员看静态代码时候认为滴水不漏,但结果还是查到吐血)
线程:
写惯了java的人,最常见的就是自己控制线程,当然到了1.6以后有成熟的线程池可以使用,原来那种全部自己搞的方式大大降低了,但是对于线程多少和队列多半还是要自己关心的。而objc中大部分时候是不需要单独去启动线程的(而且objc也不推荐自己去控制和管理线程),mainthread通过queue+event来驱动,backendthread也可以很方便的交给系统来根据负载创建和使用,真的要用线程也可以简单的创建守护线程+block模式,真的真的要有很多交互及定时定制化处理的,就用Run Loop,非常简单timer或者source input的event驱动模式)。但归结一句话:objc中非必要直接用系统现成给的,RunLoop支持自己认为高性能高实时性的一些处理(但记得这是客户端,cpu的能力是有限的,所以有时候所谓的高实时性是种无谓的假设,后面会提到)
线程安全:
每个写服务端习惯的人,到客户端开发的时候,最不能适应的可能就是线程安全,特别是有时候想想省点内存和初始化开销,对有状态的对象复用一下,就会开始顾虑各种线程安全。但不得不说的是,在很多代码编写的过程中,一定要仔细考虑一下自己的假设,因为客户端的使用,用户界面操作是一层很好的顺序化操作的屏障,也就是说你写的程序也许在逻辑上是可并行化的,但是加入了用户界面也许永远都是串行化的,当然iphone和ipad上也许就不同了,有人会以为ipad和iphone上底层的很多东西可以共享,其实ipad多用户和并行行为操作会更往服务端开发靠拢。所以线程安全需要考虑,但未必事事做到极致,因为要去权衡客户端的内存,cpu消耗,势必还是要搞一点小技巧。
远程数据和本地数据的关系:
上面说过客户端本身的不定因素要远高于服务端,其中最大的问题就是网络,网络的不稳定导致数据的获取失败,甚至是不完整的数据,这时候客户端的兼容性要做到极致基本上是不可能的,同时网络的问题也为用户体验带来了很大的挑战,这时候不得不说的就是本地数据和远程数据的关系。很多时候会发现除了初始化的过程,“最终一致性”会被使用在客户端实现模式中,在服务端最终一致性是用于不同阶段的行为操作最后得到修正,产生一致的结果,而在客户端最终一致性是指当本地数据可用时,本地数据作为唯一标准,任何操作以本地获取和更新为第一时间行为,用户体验是行为立刻被接受的(涉及到钱的另当别论),后台可以异步做真正的一致性更新操作,最后根据服务端的反馈来纠正本地的“脏数据”。同时设计的时候可以增加一个版本的概念,那么对于可能存在的用户多终端更新可以有一定的数据更新一致性保证。
内存管理:
java中很早以前大家都会复用对象,慢慢的java语言本身提高了对对象的复用管理,因此创建对象的代价就变的非常小,而复用对象带来的老数据清理的复杂渐渐让开发人员放弃了复用(除了一些不受java语言控制的资源:文件句柄,socket,数据库连接)。objc中对于UI等组件的复用或者obj的复用,当前个人还是倾向于轻对象不复用,重对象如果无状态或者少状态尽量复用,重对象状态多则可以考虑不复用。
错误及行为收集:
客户端其实非常非常需要有行为数据和错误数据收集,前者是产品化的必要指导,后者是灵异事件的最好支持,但由于网络问题,这些数据需要做业务性的规则压缩,同时要按需批量压缩上传,这块其实个人觉得是很有价值的一块无线客户端和服务端的设计。(当然做的简单了,无非就是耗电耗流量)
服务端的支持:
iOS比android对推送要支持的好,使用起来也比较简单,同时客户端os层面对应用的支持也很到位,但android的推送就需要花不少力气(记得我以前还发过做推送的一家公司直接上市的微博),另一方面,服务端api的设计还是有非常多的讲究,很多时候很多业务系统为了做到一次请求能够有足够的返回,通常情况下就会将业务的需求和接口封装混在一起,结果就是对外的接口复用性很差(但说起来无线就是需要节省网络连接次数),但实际上还是有很多做法的,TOP去年的时候我封装了REST和TQL,但REST最后我没有外推,而TQL是强力推的,而TQL当时的定位就是在无线上,其实做无线的同学如果使用过TOP的TQL就知道它的价值,当年栽树,现在乘凉,明年如果必要会继续把这块做的更强一些。
开源库:
当前我们的无线平台应用分层两层,一层是官方和三方公用的SDK基础库,一层是上层的应用库,SDK基础库基本没有依赖任何的开源库,上层依赖了不少。那对于objc的开源依赖当前是怎么判断的呢?首先objc还是一门成熟期中的语言,从它的接口支持和废弃的变化可以看出来,那么对于开源的选择最重要的是自己是否能够把控(如果让你修改是否能够修改),大部分的objc开源不象java,都比较简单的几个文件,如果用到的类库非常大也非常复杂,那么就要考虑一下,因为语言变化很快,很多早期写的方式都只适合与当时的语言支持情况,而语言的优化本身会极大提升和改变编程模式,所以偏底层的可以自己写,尽量持续跟进语言对底层的优化(线程,文件,网络,事件驱动等等),一些工具类的库可以直接使用开源的文件(适时看看是否有被废弃的方法在里面)。所以和objc开源的状况比较匹配的就是,多半都是比较简单的几个文件开源,因此如果能掌控就拿来复用,否则自己多看developer和stack overflow。
兼容性:
我想每个学objc的人一定都很少看国内的书(国外的书也差不多),因为当你看的时候,你发现书上写的范例和流程在今天你下载的xcode里面已经很难找到了,而且很多书里面用的函数,有些都标注为废弃了,所以要看文档,直接developer.apple.com就好了(最近还支持中文了),更好的是可以直接pdf下载。再回过来说兼容性,就和我刚才评论冯大师说大部分应用都要求ios4.3以上一样,非常非常感谢apple,没有让蛋疼的ie历史重演,apple做的很绝,直接从ide上就扼杀向下兼容的萌芽,不同的macx操作系统,xcode4.5及以上,已经将ios4抛弃了,这其实正常,你看看ios中那些即将要废弃,以及在某一个版本才支持的语言特性,如果不这么干,那搞死的除了程序员还有apple的审核人员。所以感叹一句,你要玩i系列的东西,那么就升级上去,来体验变化的快乐。(你升级就回不去了,此时最为开发人员来说保留几个古董级别的os版本会非常珍贵:),看着后台ios版本升级的变化,有种莫名的爽快~~~)
客户端开发和服务端开发:
写了这么些,其实无非是从一个次新手的角度来看一些objc开发的感觉,但对于程序员来说,个人感觉如果可以,你需要尝试先多一些的服务端开发经验和年限,然后再到客户端去做开发,这个时候你的经验会让你掌握客户端开发更有确定性,同时还是和吃饭吃菜一样,两边都要配合着玩,不然到了一定程度上来说技术还是会到瓶颈,因为不会用更创新的方式去思考全局的问题,只局限于某一边的场景,例如刚才说的TQL,如果客户端开发的同学就说你全给我封好,一次搞定,再增加一个fields可选返回,而服务端的人就会疲于奔命。
以上仅仅都是技术化的内容,但要说的是无线领域的开发产品的思维要求要远高于技术本身(技术是基础),所以要有靠谱的开发团队能够快速落地(最好自己深陷其中),也要有有感觉的产品团队来明白产品在无线领域真正的价值所在。产品类的不方便贴在外部,因此如果你有兴趣加入我们,你收获的不仅仅是技术,记得我们做的不是一个App,来了你就知道我们做什么了~~~
旺旺联系:放翁
Email:
[email protected]