类和结构体
在swift中类和结构体很相似,可以使用相同的语法规则为类和结构体定义属性(常量或变量)和添加方法,从而扩展类和结构体的功能。
在swift中不需要为自定义类和结构体创建独立的接口和实现文件。在一个单一文件中定义一个类或者结构体,系统将会自动生成面向其他代码的外部接口。
一个类的实例叫做对象,而枚举和结构体只能称为实例。
类和结构体对比
类和结构体的共同点:
• 定义属性用于存储值
• 定义方法用于提供功能
• 定义下标操作使得可以通过下标语法来访问实例所包含的值
• 定义构造器用于生成初始化值
• 通过扩展以增加默认实现的功能
• 实现协议以提供某种标准功能
与结构体相比,类还有如下的附加功能:
• 继承允许一个类继承另一个类的特征
• 类型转换允许在运行时检查和解释一个类实例的类型
• 析构器允许一个类实例释放任何其所被分配的资源
• 引用计数允许对一个类的多次引用
定义语法
class SomeClass { // 在这里定义类
}
struct SomeStructure {
// 在这里定义结构体
}
提倡定义类名首字母大写(UpperCamelCase),就像系统中的类一样(String、Int、Bool)。属性和方法首字母小写(lowerCamelCase).
struct Resolution {
var width = 0
var height = 0
}
class VideoMode {
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, frameRate为0.0和name为可选 String。因为name是可选类型,它会自动的被设置默认值为nil,表示“没有name值”。
类和结构体实例
创建类和结构体实例的语法非常相似。
let someResolution = Resolution()
let someVideoMode = VideoMode()
使用构造器语法生成新的实例,其属性均会被初始化为默认值。
属性访问
你可以使用点语法访问实例的属性。属性名紧跟在实例名后面,用逗号隔开,中间不要有空格。
使用点语法
![](/qrcode.jpg)
print("The width of someResolution is \(someResolution.width)")
// Prints "The width of someResolution is 0"
上面会输出width的默认值0.
你也可以访问子属性,例如VideoMode中resolution属性中的width属性。
print("The width of someVideoMode is \(someVideoMode.resolution.width)")
// Prints "The width of someVideoMode is 0"
你还可以用点语法给变量属性赋值
someVideoMode.resolution.width = 1280
print("The width of someVideoMode is now \(someVideoMode.resolution.width)")
// Prints "The width of someVideoMode is now 1280"
与OC不同,Swift中,你可以直接给结构体的子属性赋值。上面最后一个例子,直接设置了someVideoModel的resolution属性下的width属性,并不需要给resolution属性设置新的值。
结构体类型的成员逐一构造器
所有结构体都有一个自动生成的成员逐一构造器,用于初始化新结构体实例中成员的属性。新实例中各个属性的初始值可以通过属性的名称传递到成员逐一构造器之中:
let vga = Resolution(width:640, height: 480)
与结构体不同,类实例没有默认的成员逐一构造器。
结构体和枚举是值类型
值类型表示一个值会被拷贝,当它被赋给一个变量或常量,或者传递到方法中时。
实际上,在 Swift 中,所有的基本类型:整数(Integer)、浮点数(floating-point)、布尔值(Boolean)、字符串(string)、数组(array)和字典(dictionary),都是值类型,并且在底层都是以结构体的形式所实现。
let hd = Resolution(width: 1920, height: 1080)
var cinema = hd
cinema.width = 2048
print("cinema is now \(cinema.width) pixels wide") // 打印 "cinema is now 2048 pixels wide"
print("hd is still \(hd.width) pixels wide") // 打印 "hd is still 1920 pixels wide"
枚举也遵循相同的规则。
类是引用类型
引用类型在被赋予到一个变量、常量或者被传递到一个函数时,其值不会被拷贝。因此,引用的是已存在的实例本身而不是其拷贝。
let tenEighty = VideoMode()
tenEighty.resolution = hd
tenEighty.interlaced = true
tenEighty.name = "1080i"
tenEighty.frameRate = 25.0
let alsoTenEighty = tenEighty
alsoTenEighty.frameRate = 30.0
print("The frameRate property of tenEighty is now \(tenEighty.frameRate)") // 打印 "The frameRate property of theEighty is now 30.0"
注意,虽然 tenEighty 和 alsoTenEighty 是常量,但是依然可以改变tenEighty.frameRate 和 alsoTenEighty.frameRate.
恒等运算符
因为类是引用类型,可能多个常量和变量引用一个相同的类实例。(结构体和枚举不同,因为当它们赋值给常量或变量或传递到方法时总是被拷贝)。
有时需要判断两个常量或者变量是否引用同一个类实例,Swift提供了两个操作符:
等价于 ===
不等价于 !==
用这两个运算符可以判断两个常量或者变量是否引用同一个实例。
if tenEighty === alsoTenEighty {
print("tenEighty and alsoTenEighty refer to the same VideoMode instance.")
}
// Prints "tenEighty and alsoTenEighty refer to the same VideoMode instance."
等价于(===) 和等于(==) 不同:
=== 表示两个类类型的常量或变量引用同一个类实例
== 表示两个实例的值“相等”或“相同”,判定时要遵照设计者定义的判断标准。
指针
如果你有 C,C++ 或者 Objective-C 语言的经验,那么你也许会知道这些语言使用指针来引用内存中的地址。一个引用某个引用类型实例的 Swift 常量或者变量,与 C 语言中的指针类似,但是并不直接指向某个内存地址,也不要求你使用星号(*)来表明你在创建一个引用。Swift 中的这些引用与其它的常量或变量的定义方式相同。
类和结构体的选择
当符合一条或多条以下条件时,请考虑构建结构体:
• 该数据结构的主要目的是用来封装少量相关简单数据值。
• 有理由预计该数据结构的实例在被赋值或传递时,封装的数据将会被拷贝而不是被引用。
• 该数据结构中储存的值类型属性,也应该被拷贝,而不是被引用。
• 该数据结构不需要去继承另一个既有类型的属性或者行为。
以下情境中适合使用结构体:
• 几何形状的大小,封装一个 width 属性和 height 属性,两者均为 Double 类型。
• 一定范围内的路径,封装一个 start 属性和 length 属性,两者均为 Int 类型。
• 三维坐标系内一点,封装 x , y 和 z 属性,三者均为 Double 类型。
字符串、数组和字典类型的赋值与复制行为
与在Objective-C 中 NSString , NSArray 和 NSDictionary 类型均以类的形式实现 不同,在swift中,他们以结构体的形式实现,这意味着被赋值给 新的常量或变量,或者被传入函数或方法中时,它们的值会被拷贝。