说到结构体和类,还是那句话,只要是接触过编程的小伙伴们对这两者并不陌生。但在Swift中的Struct和Class也有着令人眼前一亮的特性。Struct的功能变得更为强大,Class变的更为灵活。Struct中不仅可以定义属性,并且还可以在其中定义函数,这一点比较像Class的特性了。不过Struct毕竟是结构体,它还是不支持继承等类特有的属性的。今天这篇博客就正儿八经的来搞一搞Swift中的Struct和Class。
当然,这篇博客是比较基础的,但是基础的东西才是重要的东西呢,废话不多说了,走起。
一. Struct (结构体)
结构体,说白了就是一组变量,这些变量有统一的命名。在Swift中,我们不仅可以在Struct中声明变量并为变量设置默认值,而且可以在Struct中添加相应的函数。接下来我们就要创建一个Point结构体,里边有两个属性x坐标和y坐标,并且x坐标和y坐标的初始值为0。其中还有两个方法,一个是display方法,负责输出点的坐标,并且还有一个setPoint方法,这个方法负责设置坐标点。然后我们就要去这个结构体去声明变量,并且调用其中的方法。
1.结构体类型的定义
使用struct关键字来声明我们的结构体类型,结构体类型的名称为MyPoint, 其中坐标x, y为变量,其初始值为0。并且为我们的结构体添加了一个setMyPoint()方法和display()方法。
由上面的代码片段你也许会注意到在setMyPoint()方法关键字func前边多了一个mutating关键字。在Struct中的函数,默认(不添加mutating关键字)的函数对Struct中属性只有只读权限。如果你添加上mutating, 那么该函数就对属性持有读写的权限。
2.结构体类型变量的声明以及结构体函数的使用
接下来我们就利用上述“MyPoint”结构体类型来创建一个结构体类型变量。因为(x, y)值是有初始值的,所以在初始化结构体变量时不需要为其指定初始值。Struct类型的使用和Class使用是大同小异的。MyPoint()就类似于类的构造函数。我们声明完变量,并分配内存空间后,我们对x, y的值进行打印可以看到x, y的初始值为0。具体如下所示:
我们可以调用访问权限为读写的setMyPoint()方法来改变结构体变量中属性的值,下方就是把坐标(10.0,20.0)赋值给myPoint变量,具体如下所示。
除了上述方法给结构体变量中的属性赋值外,我们还可以通过构造函数给其属性赋值。也就是在给变量分配内存空间时为其指定初始值,这一点就和类的构造函数即为相似了。具体方式如下图片中的代码片段所示:
结构体就先聊到这儿,下面开始比较重要的部分:类(Class)
二. 类(Class)
Swift作为一门现代面向对象编程语言,怎么能没有类呢。关于Objc中的Class, 请参考我之前发表的一篇博客《在Objective-C中浅谈面向对象》, 其中浅谈了Objective-C中面向对象的东西。今天就聊聊Swift中的类,虽然语言不通,但是Class还是大同小异的。本篇博客的此部分注重Swift类中的语法已经使用方式,对面向对象的思想没有做过多的陈述,因为我们的重点是在Swift编程,而不是面向对象编程。好~进入这一部分的主题。
1. 类的创建与构造器
为了简单也是秉着由浅入深的原则,接下来将把上面MyPoint结构体类型使用类的形式来实现一下。简单从语法上看两者是大同小异的。下方截图中的代码段是在上述MyPoint结构体修改而来的。改成下方MyPoint类做了两点修改,第一点就是把struct关键字改成class关键字,
下方是MyPoint类的使用方法,虽然在上述类中没有构造函数,会自动生成一个默认的无参构造函数。如下所示,调用的就是默认的无参构造函数进行的类的实例化。因为在类定义时我们为类中的属性(即类的特征)指定了初始值,所以将值进行打印就会显示初始值了。
你在类定义时,没有为其定义其他的构造函数,如果你调用了该未定义的构造函数,那么就是你的不对了,编译器就会报错了,如下所示:
接下来我们就要为我们的MyPoint()类创建构造函数了。与其他现代编程语言(如C++, C#,Java等)不同,Swift的构造函数不是与类名同名的函数,而是使用特定的函数名init()来创建其构造函数。下方就是我们MyPoint类的构造函数,函数名当然是init了。在构造函数的形参列表中,我们可以为形参指定默认值,虽然下方只是一个构造函数,但是该构造函数与他的形参列表中的默认值一组合起来,可谓是打了一个漂亮的组合拳,使用起来也是灰常顺手的。
给构造函数的形参列表指定默认值就省去了重载构造函数的麻烦。上面添加了一个构造函数,并为各个形参指定默认值,下方是其不同的调用方式,这在C++中应该重载4个构造函数才能实现的效果。Swift语言由此可见一斑呢~为之又眼前一亮,心中为之一振呢。具体调用方式如下:
2.对象的赋值与比较
在Swift中也是允许把一个类的变量的值通过赋值运算符(=)来赋值给另一个变量的。不过有一点要搞明白,如果类变量a的值赋值被类变量b,那么变量a和b就指向同一块内存区域。如果a中的实例变量中的值进行了修改,那么实例b中的值也会进行修改。为了更好的表达这个思想,我们还是来张原理图来介绍一下对象的赋值吧。具体的原理图如下所示:
上面是原理,下方就是验证。我们就声明两个变量a, b。 给a分配一个实例的空间,然后把a赋值给b。再接着就是改变a的值,观察b中的属性变化。具体如下所示:
如果要判断两个变量是否指向同一个实例,那么我们就需要使用恒等运算符(===)了。下方就是判断a是否和b指向同一个内存空间,具体代码如下所示:
3.属性的懒加载(lazy)
在Swift的类中在对类进行初始化时,要对一些属性进行初始化。如果某些属性的初始化如果非常的耗费时间,那么在这种情况下我们就可以该初始化耗时的属性声明为懒加载的属性。就是在该属性声明的时候加上lazy关键字。被Lazy关键字修饰的变量会在使用时才会进行空间的分配。下方就是一个lazy的实例。
在下方实例中,除了MyPoint类,我们还需要定义一个MyCycle类。在MyCycle类中,使用到MyPoint类。在MyCycle类中的MyPoint属性为懒加载属性,具体请看代码,如下所示:
(1) 定义MyCycle类,在MyCycle类中,定义一个属性为lazy的MyPoint类变量。如下所示:
(2)接下来就是使用MyCycle, 声明MyCycle类型的变量,并为其分配MyCycle的类型实例。由下方实例可知,在调用MyCycle()构造函数时,MyCycle类中的point属性并没有对其进行初始化,此刻的point为nil。这样就减少了MyCycle初始化的时间。
(3) lazy属性point会在MyCycle实例对象在使用point属性时才会对其进行初始化,下方是myCycle实例变量调用point属性的代码片段,这时就明确的看到point是不为nil的。如下所示:
4. 计算属性(Count Property)
计算属性这一个特性在Objective-C中也是没有的。什么是计算属性呢,一句话概括:计算属性的值可以由其他属性的值来计算得到,同时在给计算属性赋值时也可以用来计算其他属性的值。也许说起来比较拗口,理解起来也许回有些困难,那么接下来来个小实例即可明白计算属性是怎么回事了。
下方我们创建一个名为Money的类,在Money类中有两个属性,一个是存储属性(普通属性)名为CNY(代表着人民币), 另一个是名为USD的计算属性(代表美元)。在USD计算属性的set方法中由USD的值计算CNY的值,在USD计算属性的get方法中由CNY计算出USD的值,并返回。Money类的具体代码片段如下所示:
计算属性在使用时和存储属性没有什么区别,下方是Money实例来调用其存储属性和计算属性的代码段,已经结果输出如下所示。下方代码段虽然简单,但是你慢慢的去品还是很有味道的。先看第一部分,也就是第一次给USD赋值,当给USD赋值时,CNY的之会立即被计算出来。 而当我们给CNY赋值时,USD的值不会被立即计算出来,因为只有在使用USD时才会调用get方法,这时候才会根据CNY的值来计算USD的值。具体结果请看下方代码段:
5. 属性观察
属性观察是用来干嘛的呢?说白了,属性观测器就是来观察属性的赋值情况的,属性观测器包括willSet()和didSet , willSet在属性将要被赋值的时候被调用, didSet是在属性被赋值后调用,关于这两个属性观察函数,写个实例就一目了然了。由下方实例可知,在willSet调用时,property属性的值还为默认值,但是在didSet执行时,property的值已经成为被赋予的值了。
6. 实例方法与类方法
在Objc中,类方法是由+来修饰的,实例方法是由-号来修饰的。在Swift的方法中就没有+或者-号进行修饰了,但是Swift中声明方法时,多了一个class。普通方法没有什么特别之处,而类方法的声明和定义需要在关键字func前添加class关键字。下方MyTestClass中定义了一个实例方法和一个类方法,并且给出了调用方式,如下所示: