类和结构体是人们构建代码所用的一种通用切灵活的构造体,我们可以使用完全相同的语法规则来为类和结构体定义属性(常量,变量)和添加方法,从而拓展类和结构体的功能
与其他编程语言所不同的是,swift
并不要求你为自定义类和结构体去创建独立的接口和实现文件,你所要做的是在一个单一文件中定义一个类或者结构体,系统将自动生成面向其他代码的外部接口
通常一个类的实例被称之为对象,然而在swift中,类和结构体的关系要比其他语言中更加密切
类和结构体对比
swift中类和结构体有很多共同点
- 定义属性用于储存值
- 定义方法用于提供功能
- 定义下标操作使得可以通过下标语法来访问实例所包含的值
- 通过拓展以增加默认实现的功能
- 实现协议以提供某种标准功能
与结构体相比,类还有如下的附加功能
- 继承允许一个类继承另一个类的特性
- 类型转换允许在运行时检查和解释一个类实例的类型
- 析构器允许一个类实例释放任何其所被分配的资源
- 引用计数允许对一个类的多次引用
结构体总是通过被复制的方法在代码中传递,不使用引用计数
定义语法
//类的定义
class EYTestClass: NSObject {
}
//结构体的定义
struct EYTestStruct {
}
在每次定义一个新类的或者结构体的时候,实际上定义一个新的
swift
类型,因此请使用UpperCamelCase
这种方式来命名,请使用lowerCamelCase
这种方式为属性和方法命名,以便于和类型名区别
以下是定义结构体和定义类的实例
//结构体的定义
struct EYTestStruct {
var width = 100
var height = 100
}
//类的定义
class EYTestClass: NSObject {
var isBig = true
var len = 20
var name :String?
var testStruct = EYTestStruct()
func getLen() -> Int {
return len
}
}
类和结构体实例
生成结构体和类实例的语法非常相似
var testClass = EYTestClass()
var testStruct = EYTestStruct()
结构体和类都是使用构造器语法来生成新的实例,构造器语法的最简单形式是在结构体或者类的类型名称后跟随一对空括号,通过这种方式所创建的类或者接头体实例,其属性会被初始化默认值
属性访问
通过使用点语法,你可以访问实例的属性。其语法规则是,实例名后面紧跟属性名,两者通过点号(.)
连接
var testStruct = EYTestStruct()
testStruct.height = 20
testStruct.height = 10
let infoTest = EYInfo()
infoTest.age = 10
infoTest.name = "Edison"
infoTest.isMar = false
print("\(infoTest.age) \(infoTest.name) \(infoTest.isMar)")
注意,与oc语言不同的是,swift允许直接设置结构体属性的子属性,就是直接设置了testStruct属性的width这个子属性,以上操作并不需要重新为整个height属性设置新值,对比oc中的cgrect cgpoint就知道了
结构体类型的成员逐一构造器
所有结构体都有一个自动生成的成员注意构造器,用于初始化新结构体实例中成员的属性,新实例中各个属性的初始值可以通过属性的名称传递到成员的逐一构造器中
let testStruct2 = EYTestStruct(width:10,height:20)
print("=\(testStruct2)")
//reslut :=EYTestStruct(width: 10, height: 20)
与结构体不同,类实例没有默认的成员逐一构造器
结构体和枚举是值类型
值类型被赋予一个变量,常量或者被传递给一个函数的时候,其值会被拷贝
在之前的章节中,我们已经大量使用值类型,实际上,在swift
中,所有的基本类型:整数(Integer
),浮点数(Float
),布尔值(Boolean
),字符串(String
),数组(array
),字典(Dictionary
),都是值类型,并且在底层都是以结构体的形式所实现
在 Swift 中,所有的结构体和枚举类型都是值类型。这意味着它们的实例,以及实例中所包含的任何值类型属性,在代码中传递的时候都会被复制
let testStruct2 = EYTestStruct(width:10,height:20)
print("=\(testStruct2)")
//reslut :=EYTestStruct(width: 10, height: 20)
var testStruct3 = testStruct2
testStruct3.width = 20
testStruct3.height = 40
print("=\(testStruct3)")
//result :=EYTestStruct(width: 20, height: 40)
类是引用类型
与值类型不同,引用类型在被赋予到一个变量、常量或者被传递到一个函数时,其值不会被拷贝。因此,引用的是已存在的实例本身而不是其拷贝
let infoTest = EYInfo()
infoTest.age = 10
infoTest.name = "Edison"
infoTest.isMar = false
print("\(infoTest.age) \(infoTest.name) \(infoTest.isMar)")
let infoTest2 = infoTest
infoTest2.age = 20
infoTest2.name = "Edison2"
infoTest2.isMar = true
print("==\(infoTest.age) \(infoTest.name) \(infoTest.isMar)")
print("==\(infoTest2.age) \(infoTest2.name) \(infoTest2.isMar)")
/*
result:
==20 Edison2 true
==20 Edison2 true
*/
如以修改infoTest2
后,infoTest
的值也改变了
恒等运算
因为类是引用类型,有可能有多个常量和变量在幕后同时引用同一个类实例。(对于结构体和枚举来说,这并不成立。因为它们作为值类型,在被赋予到常量、变量或者传递到函数时,其值总是会被拷贝。)
如果能够判定两个常量或者变量是否引用同一个类实例将会很有帮助。为了达到这个目的,Swift 内建了两个恒等运算符
- 等价于 (===)
- 不等价于(!==)
if infoTest === infoTest2{
print("引用同一个实例")
}else{
print("不引用同一个实例")
}
let infoTest4 = EYInfo()
infoTest.age = 10
infoTest.name = "Edison"
infoTest.isMar = false
print("\(infoTest.age) \(infoTest.name) \(infoTest.isMar)")
if infoTest === infoTest4{
print("引用同一个实例")
}else{
print("不引用同一个实例")
}
指针
在c
和oc
中,都是使用指针来引用内存中的地址,一个引用某个引用类型实例的swift常量或者变量,与C语言中的指针类似,但是并不是直接指向某个内存地址,也不要求使用*
来标明你在创建一个引用个,swift
中这些引用与其他的常量或变量的定义方式相同
类和结构体的选择
在你的代码中,你可以使用类和结构体来定义你的自定义数据类型。
然而,结构体实例总是通过值传递,类实例总是通过引用传递。这意味两者适用不同的任务。当你在考虑一个工程项目的数据结构和功能的时候,你需要决定每个数据结构是定义成类还是结构体。
按照通用的准则,当符合一条或多条以下条件时,请考虑构建结构体
- 该数据结构的主要目的是用来封装少量相关简单数据值
- 有理由预计该数据结构的实例在被赋值或传递时,封装的数据将会被拷贝而不是被引用
- 该数据结构中储存的值类型属性,也应该被拷贝,而不是被引用
- 该数据结构不需要去继承另一个既有类型的属性或者行为