类和结构体是一种多功能且灵活的构造体。通过使用与现存常量 变量 函数完全相同的语法来在类和结构体中定义属性和方法以添加功能。
Swift中不需要你为自定义的类和结构体创建独立的结构和实现文件。在Swift中,你在一个文件中定义一个类或者结构体,则系统会自动的生成面向其他代码的外部接口。
注意:一个类的实例通常被称为对象,总之Swift的类和结构体在功能上要比其他语言中的更加相近。
类与结构体的对比
类和结构体的共同之处
1.定义属性来存储变量
2.定义方法用于提供功能
3.定义下表脚本用来允许使用下表语法访问值
4.定义初始化器用于初始化状态
5.可以被扩展用来默认所没有的功能
6.遵循协议来针对特定类型来提供标准功能。
类有结构体所没有的额外功能
继承允许一个类继承另一个类的特征
类型转换允许你在运行检查和解释一个类实例的类型
反初始化器允许一个类实例释放任何其所被分配的资源
引用计数允许不只一个对类实例的引用。
定义语法
类与结构体有着相似的定义语法,你可以通过使用关键词class来定义类 使用struct来定义结构体。并在一对大括号中定义它们的具体内容。
代码示例
class SomeClass {
//定义属性或者方法
}
struct SomeStruct {
//定义变量或者方法
}
注意:
无论你在何时定义了一个新的类或者结构体 实际上你定义了一个全新的Swift类型。
struct Resolution {
var width = 0
var height = 0
}
class VideoModel {
var resolution = Resolution()
var interlaced = false
var frameRate = 0.0
var name:String?
}
上面这个这个例子定义了一个名叫 Resolution的新结构体,用来描述一个基于像素的显示器分辨率。这个结构体拥有两个存储属性名叫 width和 height,存储属性是绑定并储存在类或者结构体中的常量或者变量。这两个属性因以值 0 来初始化,所以它们的类型被推断为 Int 。
上面这个例子也定义了一个名叫 VideoMode的新类,用来描述一个视频显示的特定视频模式。这个类有四个变量存储属性。第一个, resolution,用 Resolution结构体实例来初始化,它使属性的类型被推断为 Resolution。对于其他三个属性来说,新的 VideoMode实例将会以 interlaced为 false (意思是“非隔行扫描视频”),回放帧率为 0.0,和一个名叫 name的可选项 String值来初始化 。 name属性会自动被赋予一个空值 nil ,或“无 name值”,因为它是一个可选项。
类和结构体的实例
Resolution结构体的定义和 VideoMode类的定义仅仅描述了什么是 Resolution或 VideoMode。它们自己并没有描述一个特定的分辨率或视频 模式。对此,你需要创建一个结构体或类的实例。
let someResolution = Resolution()
let someVideoModel = VideoModel()
结构体和类两者都能使用初始化器语法来生成新的实例。初始化器语法最简单的是在类或结构体名字后面接一个空的圆括号,例如 Resolution()或者 VideoMode()。这样就创建了一个新的类或者结构体的实例,任何属性都被初始化为它们的默认值。
访问属性
你可以用点语法来访问一个实例的属性。在点语法中,你只需在实例名后面书写属性名,用( .)来分开,无需空格:
someResolution.width//0
someVideoModel.resolution.height//0
someVideoModel.name = "tian"
someVideoModel.name//"tian"
结构体类型的成员初始化器
所有结构体中都有一个自动生成的成员初始化器,你可以使用它来初始化新结构实例的成员属性。新实例属性的初始化值可以通过属性名称传递到成员的初始化器中。
let vga = Resolution(width: 100, height: 70)
与结构体不同,类实例不会接收默认的成员初始化器。
结构体和枚举类型是值类型
值类型是一种当它被指定到常量或者变量 或者被传递给函数时会被拷贝的类型。
实际上,Swift 中所有的基本类型——整数,浮点数,布尔量,字符串,数组和字典——都是值类型,并且都以结构体的形式在后台实现。
let hd = Resolution(width: 1920, height: 1080)
var cinema = hd
cinema.width = 2048
hd.width //1920
这个例子中 当cinema被赋予 hd的当前值 存储在hd中的值就拷贝给了新的cinema实例。这最终的结构就是两个完全不同的实例。只是碰巧有了相同的数字值。所以改变其中的一个,不会影响另一个的值。
这样的规则同样也适用于枚举类型。
var currentDirection = CompasPoint.West
var rememberedDirection = currentDirection;
rememberedDirection = .South
currentDirection// .West
类是引用类型
不同于值类型 在引用类型被赋值到一个常量,变量或者本身被传递到函数的时候它是不会被拷贝的。相对于拷贝,这里使用的是同一个对现存实例的引用。
代码示例
let tenEighty = VideoModel()
let hd = Resolution(width: 1920, height: 1080)
tenEighty.resolution = hd
tenEighty.interlaced = true
tenEighty.name = "10086"
tenEighty.frameRate = 25.0
let alsoTenEighty = tenEighty
alsoTenEighty.frameRate = 30.0
tenEighty.frameRate // 30.0
alsoTenEighty.resolution.width = 2048
tenEighty.resolution.width //2048
它们都指向内存中的同一个位置,一改俱改。
特征运算符
因为类是引用类型,在后台有可能很多常量和变量引用了同一个类的实例。(由于是值类型 相同这词对结构体和枚举类型来说 并不是真正的相同。它们在内存中是两个独立的存在)
有时候找出两个常量或者变量是否引用自同一个类实例是非常有必要的。为了允许这样,Swift中提供了两个特点运算符
相同于(===)
不相同于(!==)
利用这两个恒等于运算符来检查两个常量或者变量引用的类型是否相同
if tenEighty === alsoTenEighty {
print("是同一个类型的同一个实例")
}
注意 相同于(用三个符号表示 或者说 ===)这与等于的意义是不同的。
相同于 意味着两个类类型常量或者变量引用自相同的实例
等于 意味着两个实例的值上被视作相等或者等价。某种意义上的相等。就如同类设计者定义的那样。
类和结构体之间的选择
类和结构体都可以用来定义自定义的数据类型,作为你的程序代码构建块。
总之,结构体实例总是通过值来传递,而类实例总是通过引用来传递。这意味着他们分别适用于不同类型的任务。当你考虑你的工程项目中数据结构和功能的时候,你需要决定把每个数据结构定义成类还是结构体。
按照通用准则,当符合以下一条或多条情形时应考虑创建一个结构体:
结构体的主要目的是为了封装一些相关的简单数据值;
当你在赋予或者传递结构实例时,有理由需要封装的数据值被拷贝而不是引用;
任何存储在结构体中的属性是值类型,也将被拷贝而不是被引用;
结构体不需要从一个已存在类型继承属性或者行为。
合适的结构体候选者包括:
几何形状的大小,可能封装了一个 width属性和 height属性,两者都为 double类型;
一定范围的路径,可能封装了一个 start属性和 length属性,两者为 Int类型;
三维坐标系的一个点,可能封装了 x , y 和 z属性,他们都是 double类型。
在其他的情况下,定义一个类,并创建这个类的实例通过引用来管理和传递。事实上,大部分的自定义的数据结构应该是类,而不是结构体。
字符串 数组 和字典都是值类型。
Swift 的 String , Array 和 Dictionary类型是作为结构体来实现的,这意味着字符串,数组和字典在它们被赋值到一个新的常量或者变量,亦或者它们本身被传递到一个函数或方法中的时候,其实是传递了拷贝。
这种行为不同于基础库中的 NSString, NSArray和 NSDictionary,它们是作为类来实现的,而不是结构体。 NSString , NSArray 和 NSDictionary实例总是作为一个已存在实例的引用而不是拷贝来赋值和传递。