泛型是面向对象里另外一个重要的概念,除了多态,它进一步增加的代码的重用范围,而对于 Swift 语言来说,泛型贯穿了它的始终。
从 Print() 说起
我们从上课的第一天开始就在使用这个全局函数了,它让我们得以在屏幕上显示语句,用起来十分简单,传入一个字符串即可。
传入字符串就显示字符串,传入整形就显示数字……
等等!作为一个函数,它怎么能接收两种不同的参数!
好吧我们使用[……]
这节课我们一起来认识一下 Swift 中的错误处理
在调用方法和写一个轮子的时候,总会有各种各样奇奇怪怪的错误,就是已经正常编译的软件,也会出现一些不可预期的错误。不过,这些错误当中,有一些是可以被识别和捕捉的——它们可预期。
可预期的错误
为什么我们说有一些错误是可以预料得到的呢?比如说读取一个文件的时候文件不存在、保存一个文档的时候目录不可写、下载文件的时候网络无连接、传送一个参数的[……]
不全都是动态
我们都知道,方法是放在实例里运行的。也就是说方法执行的结果是依据实例属性而来的,而实例的属性是根据当前状态而变化的——我们称其为“动态”。
但真的全都是动态的吗?其实不是,有些方法它会直接根据你的调用而给出结果,并不需要依据实例属性的变化来产生变动——也就是说它是无论如何都会产生确定的结果的,我们称这样的方法为静态方法,它是静态的。
我们来举个栗子看看:
[crayo[……]
前边铺垫了那么多,现在终于要讲到垃圾回收这个东西了。
ARC
这不是方舟……这是自动引用计数(Automatic Reference Counting),这个东西是苹果用来管理内存的。
它的功能就是那个垃圾堆上的垃圾回收器。它能够保证所有在堆上运行的对象被释放后不会一直驻留在堆上。保证了那块内存会再分配给其他要使用的对象上。
何为释放
说到对象会不会被垃圾回收器给收走,那就要看引用会[……]
在上一节课的末尾,我们最终明确了一个对象的创建过程,那么,说起来创建一个对象的样子很像是调用了一个方法,可这个方法到底是什么呢?
初始化器
没错的,当我们初始化了一个类为对象的时候,我们确实调用了一个方法——初始化器。
初始化器其实就是一个特殊规定了的方法,它能够为类进行初始化。
想象一下,如果没有初始化器,那么我们调用的类就一定是千篇一律的,每一次都要先创建对象,再修改它的属性?[……]
是时候来看看对象和方法的生存空间了——想要了解面向对象,不知道方法和对象放在哪里是一件很尴尬的事情——毕竟我们还是要面对垃圾收集器的,想要让垃圾收集器帮你做更多的事情,我们就得一定程度上了解它的工作机制,了解方法和对象存放在哪里,否则的话,你就又要写出一个不堪一击的程序来了!
我们在前边的课程当中已经基本地提过堆的概念,没错,对象(实例)是放在堆上的——我将它形象地描述为“垃圾堆”。
被堆[……]
上节课我们提到了协议,但是只讲了它的一种应用方式,这节课我们就来深入地了解一下这个用起来和 class 差不多的协议究竟有什么高深奥义。
现在,我们要再一次回顾那个可耻的继承树:
这里我们写了武器……是用来进行攻击和防守的。那么,作为一个游戏,武器的模型不能够单单只用在这一个地方,不然的话开发的成本就太大了——我们要尽可能的榨干代码的价值。
我们与设计师沟通以后,设计师想[……]
说了方法的重写,我们再回过头来看看那个继承树:
这个看起来应该还行,我们可以创建大刀的实例,创建手枪的实例……
但是如果我要这样写呢?
|
1 |
var 武器 = 武器() |
那么问题来了:挖掘机……
不,我们的问题是“武器”到底是个什么东西?
我们说“动物”,人是动物,鸟是动物,狗、猫都是动物,那有没有个动物是动物呢?
答案是没[……]
说了那么多次的重写,这次我们就来认真的对待一下方法的重写。
合约
我们说了,继承就相当于是签订合约,我们继承出来的子类一定要遵守这个合约,那么就算你想要做一些合约里没有的事情,也要遵守合约的规范,所以,你重写方法,也一定要符合方法的类型。
我们讲过方法的类型,它以 ()->() 这样来表示。所以,重写的方法也一定要遵守这个类型即接收参数返回参数类型要相同……名字要相同还需要我说吗?[……]
上一节课我们说完了继承,那这节课我们就继续深入,来看看继承树的大招是什么。
可能我和你说起多态这个发音,你最先想到的应该是高中生物里讲的“多肽”;好吧,这两者之间唯一相同的可能就是发音了。
继承的意义
我们说继承实现的意义非凡,它大大降低了我们代码中的冗余行数,降低了代码的维护难度……
其实,我没有提到继承的另一个意义——合约。
上一节课我们做了演示,继承代表了子类获得了父类[……]
上节课我们具体地讲述了继承的机制,并且也设计了一个继承树,那么问题来了:我不是要问挖掘机技术哪家强?我是要问如何来确定一个类是另一个类的子类呢?我们又如何设计一个类而不是某个类中的属性呢?
“是一个”与“有一个”
这里我们就要用这么一个方法来检验它们二者之间的关系了:
我们说,手枪是枪械——OK,没有问题,那么手枪这个类就是枪械的子类。
还有长剑是一把剑,是一个冷兵器——没问题,那这样[……]
我们这次一起来回顾一下之前几节课里提到的继承,我们曾在初见 OOP 里用了一个开发手机(系统)的栗子来描述继承这个东西,相信大家还有印象。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
class CellPhone { func call() { print("Call someone!") } func powerUp () { print("Welcome!") } func powerDown() { print("Goodbye~") } func sendMessage() { print("sent a Message!") } } |
继承
那么这节课我们就深入的来了解了解继承这个概念。
这个其实也不难理解,你看,当你的父辈去世,那他们的财富就会由你继承——好吧,这不是一件很值得开心的事情,但这毕竟是事实。[……]
这节课我们来试试开发一个简单的命令行小游戏,来完整的体验一次所谓的“开发过程”。
游戏设定是这样的:
这是一种棋盘类游戏,我们来猜测敌人战舰的位置,只要命中数发就可以击沉它们。
我们给这些战舰贴点标签……比如各种网站吧?所以,这就成了一个攻击网站的程序……捂脸。
游戏目标
我们要玩家以最少的次数攻击网站,把它击垮。然后我们根据玩家的猜测次数来进行评分。
大致设计
我们画一个7*7[……]
我们在上课之前,一起来回顾一下以前曾提过的“SoC”的概念,我们说这个叫做“Separation of Concerns”,我把它翻译为责任分离——即不同的部分专注于自己的那一部分。或者说一个对象完成一个目标。
这样做的目标既让代码更加模块化易于维护,也让系统运行效率更高。所以说,我们要让对象之间的通信变得更加规范才行。
数据隐藏
你去看看你在写代码时候用到的那些框架,哪个给你直接展示了[……]
声明一个变量
我们使用 var 来声明一个变量,就好像从柜子里拿出了一个试管放在了实验台上;
我们给变量规定了一个类型,就好像在试管上贴上了标签;
那么放入的试剂就必须是标签上标记了的——否则可能导致中毒或者爆炸。
同样的,如果我们试图给一个储存器放入一个错误的数据类型,那么编译器就会报错——没错总有办法能够骗过编译器——反正我不会教你这个方法,那样就会导致程序崩溃啦。[……]
上一节课我们第一次领略了 OOP 的风采,于模棱两可的类和对象究竟是什么东西呢?这节课我们用一个简单的小栗子来向你介绍。
尝试解释
我们说类和对象的关系是设计图和产品的关系,就拿我们的房子来说,一栋楼肯定会有对应的设计图,但设计图绝不会只能对应一栋楼,至少一个小区肯定会用一套设计图不是吗?我们的“类”就是这个“设计图”。我们用这个设计图设计了对象的属性、功能等等的一系列内容,然后通过实例化来产[……]
考虑到有的同学没有 iPhone,但学习 Swift 语言大家至少都会有 OS X 操作系统,我们的代码演示都会在 OS X 下完成,使用 CLI 界面。这样虽然又显得古老了,但相信我去掉 GUI 会让你省心不少——因为那又是另一回事了。
那么现在,让我们来和 OOP 问个好
我们来看看,用 OOP 进行开发到底会是个什么样子?
考虑到我们现在并没有很高的开发水平,那么我们把所有的功能实[……]
我们都知道在 Java 中声明一个抽象的类或者方法要使用 abstract 关键字,可是很遗憾熟悉的东西总会逝去,在 Swift 中已经没有了这个标签。
那么,我们究竟要如何来声明一个抽象的类呢?
私有构造器(初始化器)法
这一点倒是让人很熟悉对吧?吧构造器私有了那这个类肯定就不能被初始化了,自然就不能创建对象实例……不过……哪有那么多但是,反正能用就行了!
[crayon-6903e[……]