我们在第四讲时已经介绍过class和面向对象的相关概念。如果你有其他语言的编程经验,理解面向对象应该不难。今天我们要介绍的struct也就是结构体,乍看之下会觉得和Class很像,但其实它也是一个古老的概念,早在C语言时代就已存在。不过随着JavaJavaScriptPython等语言逐渐占据C位,结构体的概念已经很少被人提起了。但是近两年,GoRust的呼声日响,struct又逐渐回归大众视野,而Swift因为与C语言的渊源,自诞生之初就提供了struct的支持。

今天我们就来看看struct,相信通过今天对结构体的学习,不仅能帮你更好地掌握Swift,即使以后遇到其他语言的相关概念,你也可以触类旁通,了然于胸。

先来看一段简单的结构体代码,就用我们在第四讲接触过的案例。

struct Shape {
    var numberOfSides = 0
    
    init() {
       print("i am here")
    }
    
    func simpleDescription() -> String {
        return "A shape with \(numberOfSides) sides."
    }
}

var shape = Shape()

这段代码几乎与我们之前写的一模一样,唯一的区别,就是把定义类的class关键字改成了定义结构体的struct关键字。所以从共性上来说,结构体是很相似的。它们都可以定义属性和函数。那么struct到底与class有什么区别呢?要理解这个问题,我们必须搞懂什么是值传递,什么是引用传递。下面我们来看一些简单的代码:

var a = [1,2,3]
var b = a

第一行定义一个变量a,并设置其值一系列数字组成的数组,第二行定义了变量b,并把a传递给b`。继续:

a.append(4)

print(a)
print(b)

此时我们把数组a追加一个数字4,然后再打印ab两个变量的值,你觉得会是多少呢?动手试一下你就知道:

CleanShot-2022-04-24-at-11.06.41

如图,ba的内容不再相同,也就是说b的值不受a变化的影响。像这样,只是在var b = a的时候,发生了一次a值到b值的传递,之后它俩天各一方再无瓜葛,中间的那次=号赋值就是值传递

值传递相反的就是引用传递,这个必须借助class才能演示,我们来看下面的代码:

class Circle{
    var radius:Int
    
    init (_ r:Int){
        self.radius = r
    }
}

我们定义了一个类,用来描述一个圆形,它有唯一的一个属性radius(半径),同时我们可以用构造方法直接提供半径的大小。下面我们定义一个圆形:

var a = Circle(1)
print(a.radius)

显而易见,上面的代码会输出1。那么我们故技重施,在准备个b,然后去修改a的半径,代码如下:

var b = a
a.radius = 2

print(a.radius)
print(b.radius)

大家觉得输出会是什么呢?建议动手实验一下,其实我们得到的输出是:

CleanShot-2022-04-24-at-11.10.27

对,此时ab的半径都是2了,也就是说,改变a,也就意味着b的改变,反过来也是一样的,比如我们继续执行:

b.radius = 3
print(a.radius)
print(b.radius)

这样之后输出也都是3,其实此时已经不需要再纠结是a影响了b还是b影响了a,因为**a就是bb就是a**,而之所以有这样的结果,就发生在那行var b = a,关键就在于,这个=赋值实现的是引用传递

简单总结下值传递引用传递的区别,值传递就是值复制传递,先复制一份再传递;引用传递就是值共享传递,传递是引用,而共享的是。那么什么是引用引用到底有什么关系呢?

这里给大家举个例子,你想邀请朋友来你家玩,你家这个物理意义上的一个具体空间就是一个值。而你为了能让朋友找到你家,你抄了好多小纸条,纸条上写清楚了你家的具体地址。这一张张小纸条,就是引用,把小纸条分给你朋友的过程就是引用传递

现在关于=起到的两种传递效果大家应该很清楚了。那么什么时候发生的是值传递?什么时候发生的是引用传递呢?其实非常简单,如果是class所制造的各种对象,那么传递就是引用传递,反之就是值传递

除了值传递引用传递的区别,structclass最大的区别就是前者无法享受后者的继承特性。

理解了这些区别之后,真遇到开发的具体场景,你可能还会有疑惑,针对某一个具体场景,到底该选择struct还是class呢?一个简单的技巧告诉大家,就是优先考虑struct,当struct无法满足需求的时候,再用class来替换。因为从struct改成class很容易,只要替换一个关键字就行,但是反之往往会遇到困难。当然如果你真这么操作了,也别忘了前文提到的值传递引用传递的区别,相关的代码也应检查一下。


参考资料:

往期回顾: