声明一个变量
我们使用 var 来声明一个变量,就好像从柜子里拿出了一个试管放在了实验台上;
我们给变量规定了一个类型,就好像在试管上贴上了标签;
那么放入的试剂就必须是标签上标记了的——否则可能导致中毒或者爆炸。
同样的,如果我们试图给一个储存器放入一个错误的数据类型,那么编译器就会报错——没错总有办法能够骗过编译器——反正我不会教你这个方法,那样就会导致程序崩溃啦。
所以说,试管有容量,我们声明的类型也有大小限制,超限也会报错——一般够用的。
这就是为什么会有个术语叫做“溢出”。
如果你对化学不感兴趣
那好,我们换一个栗子:
我们都知道有一些动物是吃草的,而有一些动物则只吃这些吃草的动物?
那么我这里有狼和羊,狼要吃羊,而羊要吃草。
你给狼吃草或者给羊吃狼都是不对的………………
好了,我们还是下一个话题吧。
对象不是值
现在,我们知道了储存器是什么,我们再来说说“放”到储存器里的另外一种东西——对象。
在接触了面向对象编程之后,我们就必须面对一个新的概念——什么是对象,当然,这个问题我们在上一节课已经讨论过了,这节课我们重点来讨论如何把大象放进冰箱的问题。
当我们这样声明:
1 2 3 4 |
class MyClass {} let obj1 = MyClass() let obj2 = MyClass() |
难道不是把实例放进了储存器里吗? ——还真不是。
实际上还真的没有能储存对象的储存器,就像我们说的,也没有能放进大象的家用冰箱。
悄悄告诉你,对象其实放在“堆”上……没错,就是垃圾堆。
好了,这里我们要请出除了 nil 这个特殊的值以外的另一个特殊的值了:
引用
其实引用就是指针。只不过它把指针给包装了——具体包装成了什么我们不清楚——恐怕只有苹果的攻城狮能告诉你了。总之,我们把这个值叫做引用。
不同于 C 语言的指针,虽然引用也是一个指向堆里边某个对象的指针,但它的的确确是不能被直接编辑的。
电视机和遥控器
我谈起这两个东西你可能又觉得古老……正是因为它古老,所以我觉得可能所有人都应该是接触过的。
当你生成了一个对象,那我们就说你买了一个电视机;那么对应放到你手里的,自然就是遥控器啦。所以我们说,不论你的生成的对象实例是多么复杂,多么庞大,它的遥控器永远是那个样子——顶多多了几个按钮(方法)而已,真正的实例其实是放在堆上的。
说到堆
我们再来说一说储存器里的引用与堆上对象实例的对应关系。
一般我们声明一个储存器,然后赋值给它一个对象,那么这个储存器就存了一个引用,然后对象的实例被扔在了堆上。那么,这时候我们的遥控器和电视机是一对一对应的。
就像这样,我们从同一个类生成了两个实例,两个实例被放在了堆上,而我们手中的变量则存了它们的引用——就像是门牌号。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
class A { var num = 0 func doSomething() { print(num) } } var objA = A() objA.num = 9999 objA.doSomething() //9999 var objB = A() objB.num = 2222 objB.doSomething() //2222 |
那如果我要把引用复制一份给别人呢?
1 2 3 4 5 6 |
var objC = objA objC.num = 8888 objA.doSomething() //8888 objC.doSomething() //8888 objB.doSomething() //2222 |
没错!那么另外一个变量也就指向了堆上相同的对象!
现在其实 objA 和 objC 是同一个对象的引用呢!
现在我这么做:
1 2 3 4 5 6 7 8 |
var objC = objA objC.num = 8888 objB = objC objB.num = 4444 objA.doSomething() //4444 objC.doSomething() //4444 objB.doSomething() //4444 |
哈!都成了同一个对象!……等等,那 objB 中原来的对象哪里去了?这样岂不是再也没办法访问到它了吗??
没错,这个时候 objB 所引用的原来的那个对象就孤零零的存在于堆上——没关系,它早就被 GC(垃圾收集器)给带走了(别问我带到了哪里)。
做个栗子吧!
其实,就是上节课的课后题啦~
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
import Foundation class Echo { var count = 0 func hello() { print("hellooooo.....") } } var e1 = Echo() var e2 = e1 //把 e1 的引用赋值给 e2 //var e2 = Echo() var x = 0 while x < 4 { e1.hello() e1.count = e1.count + 1 if x == 3 { e2.count = e2.count + 1 } if x > 0 { e2.count = e2.count + e1.count } x = x + 1 } print(e2.count) |
运行结果是24而不再是10.
课后题
我们声明一个叫做 HeapQuiz 的类,这个类只包含一个属性就是 ID,我们用它来区分实例,现在我们生成了5个实例,然后把它们的引用打乱,现在,你能说出这五个引用对应的实例的 ID 吗?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
class HeapQuiz { var id = 0 } var x = 0 var hq:[HeapQuiz?] = Array<HeapQuiz?>(count: 5, repeatedValue: HeapQuiz()) while x < 3 { hq[x]?.id = x x++ } hq[3] = hq[1] hq[4] = hq[1] hq[3] = nil hq[4] = hq[0] hq[0] = hq[3] hq[3] = hq[2] hq[2] = hq[0] |
转载请保留出处和原文链接:https://www.logcg.com/archives/1109.html
Comments