iOS程序员入门一年记 —— 学习篇

文/某鸟

前记

不知不觉,从正式决定学习 iOS 开发到现在,已经差不多一年的时间了。虽然总觉得自己还是个新人,可开发经验的确是有点,再天天的装逼说自己是个小白,也是有些不要脸了。所以打算坦然戴上 “iOS 一年开发经验” 的帽子,总结总结这一年来零零散散的经历和感悟,也把自己的一些经验和观点拿来分享,由此做个界定,继续走上一条不归路。

自学与培训

iOS 开发学习的第一个选择往往是自学培训。自学的一般不是不信邪就是穷,这是不言自明的。在学习初期,遇到的新手朋友们经历各异,有刚毕业的应届生,有干了几年其它平台想转行的老程序员,也有风马牛不相及的兴趣转行青年。

相对来说,选择自学的人还是比较多,一部分原因是现在的平均学历普遍较高,大家的自学经历比较丰富,估计都是期末赶考的行家。另外一方面,财力有限,而现在的培训费用普遍虚高。虽然各大培训机构时不时打出来“先就业,后付费”的广告,但实际的投入,又是另一个层次的不言自明了。

笔者自己也是自学入行,直到最近认识一些正在或刚刚结束培训的朋友,才对培训有了一个大致的了解,当然,
转自他人之口,认识还是很局限,但不妨拿出来一起聊聊之间的区别。

自学是伴随着各种各样的无知开始的,SwiftObjective-CC,从零开始的时候从哪开始好呢?要不要买 Mac 呢?应该买什么样的书,哪个网站会有不错的资源,哪个版本的教学视频比较靠谱?偶尔也会冒出来“学3个月能不能找到工作”这样没有依据的问题。如果没有一些靠谱的引导,自学的坑总是出奇的多。有些朋友是在职学习,时间本来就少,还需要消耗大把的精力用来拓荒。

一般遇到朋友问这样的问题时,我会先问问之前有没有什么相关经验。如果没丝毫经验,我会推荐他先去学 C 语言,一方面是因为 C 语言相对比较容易入门,可以比较快的对开发有一个笼统的认识。
另一方面也可以测试一下自己对于编程的悟性如何,以便对自己未来的学习效率有一个估计。最后,因为 Objective-C 本身是 C 语言的超集,把精力分在 C 语言上,总归不会走弯路。

而对一些有相关经验的朋友,就要分情况对待了。按照自己的已经会的东西找方向去拓展,比如面向对象的编程方式,比如 Objective-C 的语法特性。按照已有的基础去拓展,会稍微省力一些。一方面比较异同,印象比较深刻;另一方面,学过第一门语言,写第二次 Hello world! 总归会少很多需要拓荒的东西。

自学是个从无到有的过程,得步步为营,切忌贪多。尤其是方向不明确的时候,一方面不想莫名其妙的走了弯路,另一方面也打击自己学习的信心。在瓶颈期的时候多理理当下的学习重点,然后对之前会的东西做一些更深入的思考,会比较容易绕出来。尤其是在实践之后更深入的思考,哪怕是在一个点上理解的更透彻,马上就会有一种融会贯通的感觉。这种层次上的提升,会开拓自己的视野,让你用不同的角度来审视开发,成就感嗖嗖的~

另外一点自学经验是,开发入门从来都是高不成低不就的。好不容易用 Objective-C 写出了自己冠名的第一个类,可还是下不知内存管理,上不知如何构造界面。明明已经下了很大的功夫,却好像还是什么都不会的样子。这种经历比较容易惹人心慌,要学会把握学习阶段,知道自己已经会了什么,距离目标之间,还需要学会哪些知识点,然后逐个击破。

在自学过程中,对自己的心性,和对学习过程的把握会显得很重要。而且送一句忠告:因为是自学,做好在任何情况下发现自己是个傻逼的心理准备。笔者前两天刚在 GitHub 上丢了回人,才知道在类方法中,self 指向的是这个类而非实例,惭愧不已。

而反观培训过程,一方面有人督促,一方面分段目标与进度都比较明确,上面说到的坑基本都是不填自平的。而且一群人坐在一起,很容易知道自己的优势和弱势,也就容易查漏补缺。再者,有一个相互讨论的环境总是更容易发现自己一个人下意识回避的盲点,也是一个挺不错的方法。

不过有一点需要强调,就是不要对培训报过高的期望。国内 iOS 开发的就业环境虽然不错,但总归不会有宣传出来的那么夸张。在实际招聘中,对培训出身的童鞋并不会给什么特别的优待,偶尔还会有一些轻视,所以记得一定要用实际的开发能力去打动面试官哦ಥ_ಥ

近来偶尔听朋友们说自己的培训经历,其实培训的侧重点还是挺不错的。因为老师也大多是前、现从业人员,所以学习重点与实际的开发工作关联性也比较强。另外部分培训针对零基础童鞋会有一些 C 语言和基础面向对象开发的预热,过渡比较平滑,对于刚接触开发的童鞋还是比较友善的。虽然作为一个自学出生的程序员,总是有意无意的黑培训,但在实际上,不得不承认,工作技能的培训,从来都是务实的,只不过收费= =

所以总结下来,建议是如果基础薄弱、且财力尚可的童鞋不妨考虑考虑靠谱的培训机构,仅当有一定开发经验或对自己的自学能力有自信的同学可以尝试自学。还是那句话,自学的坑从来都是出奇的多,一定要做足心理准备。

研究、提问与探讨

在找到工作约莫一个月的时候,觉得自己也可以解决一些简单的问题时,就偶尔去 CocoaChina 的论坛里转转,回回帖。自己也加入了一个开发新手群,常常会在里面讨论一些相关的问题。

但是回帖这种事情很快就尝到了疲倦感,简单的问题往往惊人的相似,止不住的想复制粘贴。另外一方面则由于提问给出的信息有限,解决的过程一部分靠推断一部分靠猜,虽然本着自学坑一个不落踩过来的庞杂经验给了一个靠谱的建议,但有心无力,最后那句话仍然是“你再看看吧……”

在不断有问题产生的时候,独立研究的能力会比较重要。在初学阶段,比较困难的地方在于没有足够的理论作为依据来进行合理的假设,实在不知道为什么就会开脑洞估计是“今天天气不好,所以才会报错”的吧?这里除了重申基础的重要性之外,还得祭出法宝:一点一点试。

很少有语法还写不好的童鞋就学会使用断点Debug 工具的,所以笨办法就是注释,频繁的逐句注释测试。来验证每一句的正确性,另一方面,强化自己的基础并开始涉猎 Debug 工具。然后就发现原来可以增加“异常断点”,可以在中断情况下查看范围内的变量值,可以逐句运行。一方面,独立研究等同于实践经验,另一方面也会增加自己的 Debug 功底。

如果实在搞不定,那还是得问。但是咱既然问问题,就要本着能拿到答案的方法来。其实挺痛恨截图的,一口气截图一大段代码,然后没有上下文,后面跟一句,错哪了?连粘过来自己复现都不给机会(¯﹃¯)。然后就只能一长串的追问,把报错粘上来,把上下文粘上来,balabala…最后发现,卧槽,你属性是个 nil 啊!ಥ_ಥ

所以,合理的问问题方式:如果你能够定位问题,那就定位,如果能够做合理推理,就把自己的推理也附上去。更重要的是,记得附上报错,或者 warning 的截图。总之,能推断出问题的依据,越多越好,一般当你搜集到这么多东西的时候,估计你自己就找出问题所在了。所以,换句话说,多自查,先确保你自己确实找不出原因时(大部分是因为粗心哦我说= =),再提问。如果最终得到的结论是因为自身的知识点盲区,理解有限,或者是因为未知的知识点,那么可喜可贺,你问了一个对于自己来说很有价值的问题。可如果是因为粗心 = = 我不怪你,你自己去面壁就好了。

之前遇到的一个比较蛋疼的问题是 NSLog(@"%@ %@",string1),string2;,贴出来一段代码说检查变量都没问题,但就是打印崩溃。然后一群人在上面的代码里找了好一阵,最后说你把打印的那句贴出来。刚开始大家都以为又是某个“黑魔法”一样的诡异语法,结果自己在代码里一敲,才发现根本就是少了一个打印变量,被后面的逗号运算符蒙混过去了。然后就在群里吐槽,你真的不看 warning 的么?事后想起,还是一阵菊紧。

另外,尽量不要提用来偷懒的问题。有心气的时候笔者总是会帮忙百度给个链接或者是文档地址,到后面基本都是甩一句“查文档”或者“自行百度”了。这个情况倒也不是那么绝对,随着有了一个固定的讨论圈,对大家的开发经历也有些了解,有时候遇到之前朋友解决的类似问题时,也偶尔会偷偷懒,直接从朋友那里要段代码走人。建议把握住两个要点,一个是不会让对方很费事,另一个是你得确定如果偷不到懒,自己也可以解决这个问题。第二个更重要些。

就自己的经验而言,有一个稳定的圈子讨论和提问是一个很好的学习途径。每个人都会有盲点,而有价值的话题点能够很有效的帮助你规避这个问题。不同的视角,不同的开发经验,会带来许多灵感和靠谱的经验,也会很实际的告诉你有哪些你还不会但是必须要会的东西。

补上一条,不建议大家本着与自己无关的态度来对待提问,哪怕是在自己懂的东西还很少的时候。提出一个有价值的问题不容易,推断问题的关键点,涉及的知识点,然后把相关的知识点系统化,整个一套流程走下来,本身就是对独立学习能力的提升。而且当你给别人讲述的时候,往往会很容易发现自己比较含糊的地方,那恰恰就是自己的薄弱环节,千万不要错过这样的机会,因为一旦错过了,下次遇到或许已经是上线的 crash 了。

英语

前两个小节比较新手向,随着开发时间增加,一些刚开始察觉不到的问题就会暴露在眼前。

英语是开发路上的拦路虎,越是想走远,就越是不得不正视。

还记得自己第一份工作的第一个月,不完全统计每天至少对着官方文档3~4个小时,那种崩溃真的是无法言喻。好在 Mac 的三指查词功能很方便,等价于一手拿着字典一手看文章。看了那么一段时间后,对于看英语的文献就不再那么排斥了。

专业英语和普通英语区别最大的地方在于特定词汇的使用。像 OOPObject Oriented Programming ,这样的专业词汇甚至缩写倒不会引起太大的阅读障碍,哪怕不知道是什么,百度一下也就得到答案了。但某些约定俗成的常见词就成了阅读期比较不容易掌握的坑。

比如 “declare” 和 “define”,翻译过来分别是“声明”和“定义”。如果是在“先声明,后调用”的这个环境下,你会发现用“先定义,后调用”来解释也是完全说的过去的。但在实际文献中,两个词的不同远远不止翻译区别这么简单。

如果你接触过 Python ,他是用 def (define 的缩写)来定义函数的,而在 C语言 系中,则是用 declare 来表答函数和变量的声明。我们利用两种语言的报错来展示这个小细节。

1
2
#python
print a

Traceback (most recent call last):
File ““, line 1, in
NameError: name ‘a’ is not defined

1
2
//Objective-C
NSLog(@"%d",a);

Use of undeclared identifier ‘a’

当然,如果是这样,是达不到混淆的效果的。只可惜,在 Objective-C 中,你也能看到 define 的出现,在哪呢?宏定义 #define

1
2
3
//Objective-C
#define a 1
#define a 2

‘a’ macro redefined

如此一来,如果我们对以上两个单词不加以区别,而是简单的依靠查单词的话,就很有可能在特定的环境下出现理解失误了。可反观之,当我们确切掌握了这些词在专业环境中的含义,那它们便能成为我们理解文章含义的一把钥匙,不止不会错失细节,反而更容易根据这些词来推断其它不相熟的内容。

这里有一个比较典型的例子是 unrecognized selector sent to instance 这句报错。在新手中往往很容易忽略,看到了也不以为然,然后抓耳挠腮也想不到到底哪里出了错。可这类错误的原因其实往往都来自很低级的错误:.h文件中的方法名和.m文件中不一致。比如之前看到的一段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
@interface Man:NSObject
-(void)setID:(int)id
age:(int)age
sex:(int)sex;
@end
@implementation Man
-(void)setID:(int)id
sex:(int)sex
age:(int)age{
//...
}
@end

这么写本身静态编译虽然会给一个 warning 提示没有实现之前声明的方法,但是 Build Seuccess 是肯定的。所以只有当调用这个方法的时候,才会报错。通常的做法都是先从调用这个方法的上下文找起,然后找不到错,就在这个方法的实现里找找看。最气急败坏的地方在于给实现的第一句打了断点,结果还没进来就报错了,可之前的所有代码哪一句都没问题。郁闷么?是啊,得花多少时间才能发现是方法名写错了,发现之后是不是很有扇自己耳光的冲动?

可如果你知道了 unrecognized selector sent to instance 这句报错的意思是实例响应了错误的方法(你也可以翻译成“未被承认的选择器被发送给了实例”,其实一直挺想吐槽这句报错来着= =),那么你首先要确认的就应该是 selector 是否正确。而什么是 selector 呢,如果写代码时有留意的话,在 addTarget: action: 这个方法中,action 的参数有一个 @selector 标记。当然了,还有 respondsToSelector 这种“耍流氓”语句的起手势,它们都在向你透露同一个信息,selector,其实就是方法名。而方法名是以 .h 文件中的方法声明为基准的,那还不麻溜的去检查 .m 文件里的方法名?

在以前学英语的时候,老师总是说,同一个意思要学会用不同的方式去表达。但是在专业文献中,词汇的指代往往准确而单一,哪怕是 push 这样口语化的词,在 iOS 开发中也是有很确切的含义的。对这些词的积累会很有助于阅读,哪怕你不上 Google,不上 StackOverFlow,也不看英文文献,不看官方文档,就是只为了看懂 warning 和报错,也是很有必要的。

在 Objective-C 设计之初,为了让整个语言更加易读,特地设计了多段式的方法名,来说明每个参数。而得益于 Xcode 强大的自动补全能力,其命名往往长而全,会利用一些关键词来表达其功能。这使得我们在阅读代码的同时,也能够有效的积累对应的表述用词。所以千万不要因为自动补全的便利就忽略对类和方法命名的积累,记得多了,一方面看源代码再也不用频繁的查询文档,另一方面,你会发现大家在英文文献中有意无意的使用这些词汇,在简要的叙述中其实已经给了你代码级的步骤,这得算意外收获。

除了阅读和积累之外,另外一个有效的方法是翻译。翻译是一件逼着你去纠结每一个单词含义细节的东西。翻译一旦不准确,上下文立马就会显得不连贯。而翻译的过程,会潜移默化的加快你的阅读能力。

比如笔者第一次翻译文献的时候,还处于第一遍翻译逐个单词,第二遍拼成整句,第三遍润色文字的阶段。简单来说,大量的单词不认识,让自己无法聚焦于整句的含义。而拼成整句的过程,又无法很好的衔接上下文。于是基本到了第三遍,翻译的文字才能基本呈现出文章本身的意思。比较遗憾的是,在第一次翻译 objccn 的文章的时候,我就处于这个阶段。

后面的时间里,自己又试着翻译了一些别的文章,慢慢做到了可以在第一遍的情况下将整句的语序和含义都搞定,而在第二遍的时候借由第一次的通读全文对上下文做出一些判断。到了第三遍的时候,文章已经有了一定的可读性。对这样看似不起眼的进步,自己还是异常欣慰的。记得不知道从什么时候开始,突然发现一篇英文文章中到处都是看似生僻的专业词汇,可自己就是知道它们的含义。相对于成就感,自己更多的是一种难以置信。作为一个学渣,看到满屏幕的字母从来都应该是挠头强忍困意,看一遍就能了解其大概意思的情况根本不敢想象。

除此之外,笔者还报名了英语培训班,可以面对面的和外教进行交流从很大程度上弥补了英语使用生涩的硬伤。虽然因为工作,可以去上课的时间总是很有限,但经过近半年的积累,自己的听力和理解力还是有些许进步的。就如当初天书一般的代码现在可以在脑海中复述,并且下意识的敲出,技能的熟练总是让人欣喜且知足,也知道了还有很多东西要学,很多路要走。

当然了,即使是这样,笔者自己的英语水平依旧很差。很现实的例子是,如果在 StackOverFlow 上提问,估计还是花大半天只能憋个屁出来。当听和说开始慢慢的不再成为障碍后,对说和写的加强就显得迫在眉睫。如果有朝一日能够与大神们交流,或者去趟 WWDC,至少不因为英语拦住了路。

代码是世界的,代码也是英语的,现实从来都是这么残酷。

如何提高

瓶颈期从来都是迷茫的,长时间的迷茫可能造成工作不积极、终日碌碌无为,便秘,生活不和谐等种种的恶劣影响。而如何提高,逐渐演变为诸位 iOS 新手程序员自入职以来的第二大难题,第一大当然是找对象,别问我为什么。

一般而言,网上提供的大多数攻略都可以归纳为三条:

  • 多看网站与博客

  • 多写代码,多思考

  • 学好英语

由于简短且步骤性不强,所以实际操作性大多看个人造化。另外这种建议没有因材施教的特性,太过普遍,真正遇到问题的童鞋还是得自行解决。作为一大段伪干货,以下是我的一些个人建议,别打算照抄照搬,结合自己的实际情况,看完你就知道我这是在劝你了。

自己在开发自学路上信奉的原则主要有两条:

  • 你现在看到的所有东西都是前人写出来的,他们能做到你做不到的事,最主要的原因是因为他们会你不会的东西。

  • 时间和精力,永远是进步的不二法门。

数据结构里有一个经典的术语叫做“遍历”,破解技术里有一个经典方法叫做”穷举“,说白了就一件事,挨个试。不管拓扑模型是否复杂,也不管数量级有多恐怖,先试再说。且不说刚接触 Interface Builder 的时候,把右侧的所有控件先全部拖出来摆弄了一遍这种幼稚的行为。至少在阅读文章的时候,看到一个新的作者就点进他的博客看看他还写过什么东西,然后不求逐篇拜读,起码看到自己恰巧听过且不熟悉的内容,不要放过。如果能总结下作者擅长的技术方向,和主要讲述的技术方向,然后填个收藏且加个 TAG,留待以后遇到相关问题的时候再来拜读,也算是下了功夫了。

阅读习惯的养成需要循序渐进。刚开始入门的时候,大部分文章其实是看不懂的,这个时候过多的阅读量除了打击自信,就是认几个名字。所以刚开始的时候推荐去找一些介绍 iOS 开发知识点介绍的文章来看。网上流传着几张详细的知识点划分,可以当做参考。在这个阶段,我们只需要大致的理解这些概念属于 iOS 范畴,粗浅的了解它们是什么就够了,哪怕对定义的理解有偏差也是允许的。

等到名字认的差不多了,总会有离自己现在学习的地方恰巧是踮着脚尖才能够到的知识点。对,就从它下手,从已有的知识点起步去做拓展和完善,然后反过来校正已有的知识,百试不爽。这个时候可以就可以开始自己的阅读之旅了,遇到距离近的文章就开始读,遇到离自己远的文章别急着跳过,记得收藏和 TAG 哦,可以考虑使用 Evernote 的 Chrome 剪藏插件和 Pocket 作为备份工具。

其实在什么都不会的时候学东西是最爽的,因为什么都不会,所以也不用挑拣,反正都得学= =

当逐渐的对整体框架都开始有所把握,也就是不再会出现特别大的定义偏差时候,就要开始逐项突破了。选择困难症怎么办?不知道哪个知识点更重要怎么办?在这里我给出的建议是,从小知识点做起,比如 block 语法 , 代理/协议 的代码实现 ,这些开发初期容易遇到的小坑,它们不涉及特别复杂的底层原理,也不需要太多额外的知识点作为支撑。选择小知识点的主要原因是,先把吃透某个知识点的过程走一遍,去体会从定性到定量的过程。

技能的提升无法依靠长时间重复的工作。就像你用筷子吃饭到现在,也没办法夹住一知飞在空中的苍蝇一样。提升讲究的是短时期、有目标、高强度的训练,具体到开发,就是先举一反三,再总结归纳的过程。达到的程度就是你心里已经有一杆秤,了解所有的规则和界限,“这样写肯定是对的”。至于怎么学呢?_请学着暴力点,把所有错误的可能性都试出来,知道所有不对的方法,你就知道什么是正确的了。至于为什么只有这些是正确的,请参见你那些年看过的博客。

笔者个人不太相信什么速成与捷径,缩减意味着取舍,而知识点的缩水就代表了会有盲区。与其学个一招鲜吃到老,不如老老实实把所有可能性都踏踏实实过一遍,在实际体会过一遭之后,自然会有个结论。

另外,随着不断的试错,你会很明显的感受到最真实的规则。知道什么是完全不可行,什么是官方不推荐,什么是虽然逗逼但是可行。也知道 “Build Success” 其实根本不说明问题,知道除了编译器,其它一切都不具有最终解释权。

当你有了这样的试错经验后,你脑海中的问题就逐步从“什么是什么”转变成了“为什么会是这样”。这个时候,就是广泛阅读的开始了,因为你已经掌握了实际的求证能力,而且对文章有了辨别依据,知道哪些部分是干货,哪些部分是作者在装逼和意淫(好吧,暴露文风了= =)。

这里没有对广大技术文章作者不敬的意思,无论如何,花费时间和精力去分享知识都是一件值得肯定的事情,但事实是作者本身也有水平高低之分,文章中也偶尔有欠妥之处。我们不能强求每一位作者都写出来色艺俱佳的文章,所以要懂得在阅读的同时,别急着轻易相信。

在经历上述过程之后,诸位其实已经可以成为一个入门级的 iOS 开发程序员了。毕竟开发这件事,是可以照猫画虎来解决问题的。当你学会了 []:,就能够编写出第一个方法的调用;学会了 @interface@implementation ,就可以编写出第一个属于自己的类;学会了 Interface Builder 里的拖拽,就可以构造出第一个属于自己的界面,然后写一个计算器已经不再是问题了。

所以开发工作随着熟练一定会变成根据需求来选择对应解决方案的过程,而提升就是解决方案的积累以及这些方案从开发到使用的成熟度的进步。

后面的学习进度根据自己的需要和兴趣就好,掌握了方法,获取知识多半是水到渠成的事情。一般来说,ARC/MRC (内存管理相关)、runtime (运行时相关)、GCD (多线程相关)是 iOS 面试的三大杀器。由于涉及概念比较底层且复杂,而且在具体的代码环境里细节比较琐碎,让你复述原理已然算是便宜你了。另外 UIKit 的整体结构并不只有 UIViewUIViewController 这两个基类这么简单,掌握深层次的触发、响应、绘制都是必修课。而随着 SDK 的升级,其实还提供了很多便利的方法,切记在诸多文章中寻找蛛丝马迹。

更优秀的学习方法大概就是学习与 WWDC 等同等级的视频与文章了,笔者比较菜,对于这个高度也是可望而不可及,所以还请参考各路大神的分享,要是写几篇拓荒文顺便给我发个链接那就再感谢不过了。

以上都是我一路走来的一些自学经验,如果你仔细看,不难看出,其实都是些笨办法。不断的试错,不断的阅读去找有用的部分,而且最大的局限可能是自己的毅力和想象力。如果觉得会这些就够了,或是试错的时候无法做到痛击灵魂般较真和深刻,那么得出的效果不止不理想,还很浪费时间,这也是我前文说仅供参考的原因。

不过,如果没有更好的方法,不妨试试看,学会了骑自行车,就很难再摔倒。麻烦总好过学不到东西。

后记

不知不觉,已经两个月没有更新过博客了,而自己的新工作也进入了第四个月。新工作是一次难得的机遇,几个月以来的经历也确实是收获良多。只不过对应于收获,自己在工作上投入的时间和精力与之前也不是一个数量级,不止博客更新放缓,很多之前有时间做的事情也都断断续续的停掉了。

虽然把这篇博客作为某个新系列中的一篇,但是什么时候写下一篇,会不会有下一篇,都是个未知数。而之前博客中的两个大坑,想要回过头来填完,偶尔会觉得此生无望。

什么也不敢保证,仅希望本篇文章能够帮到一些即将或正在入门 iOS 的开发者童鞋们就好。