知识点
- 基本概念
- 结构体的基本使用
- 结构体构造器(构造函数/构造方法)
- 结构体扩充函数(方法), 又称成员方法
- 结构体是值类型
1. 基本概念
1.1 概念介绍
结构体(struct)是由一系列具有相同类型或不同类型的数据构成的数据集合
结构体(struct)指的是一种数据结构
结构体是值类型,在方法中传递时是值传递
Swift中的结构体是一类类型, 可以定义属性和函数(甚至构造函数和析构函数等)
结构体的格式
struct 结构体名称 {
结构体属性和函数
}
2. 结构体的基本使用
2.1 Swift要求实例化一个结构体或类的时候,所有的成员变量都必须有初始值
- 如果结构体的属性有默认值, 可以直接使用
结构体名称()
实例化一个结构体
// 所有成员属性都有默认值, 直接使用 结构体名称() 实例化结构体
struct Rect {
var width:Double = 0.0 // 结构体所有成员属性都是有初始值
var height:Double = 0.0
}
var r = Rect() // 所以这里可以直接使用 结构体名称() 实例化结构体
print("width = \(r.width) height = \(r.height)")
//输出结果: width = 0.0 height = 0.0
- 如果结构体的属性没有默认值, 必须使用逐一构造器实例化结构体
// 如果结构体的属性没有默认值, 必须使用逐一构造器实例化结构体
struct Rect {
var width : Double
var height : Double
}
// 这里必须使用逐一构造器实例化结构体
var r = Rect(width: 10, height: 20)
print("width = \(r.width) height = \(r.height)")
//输出结果: width = 10.0 height = 20.0
- 结构体属性的访问使用点
.
语法
struct Rect {
var width:Double = 0.0
var height:Double = 0.0
}
var r = Rect()
r.width = 100
r.height = 99
print("width = \(r1.width) height = \(r1.height)")
//输出结果: width = 100.0 height = 99.0
- 系统结构体的创建方式
// (CGPoint/CGSize/CGRect)
let point = CGPointMake(100, 100)
let point1 = CGPoint(x: 100, y: 100)
let size = CGSizeMake(100, 100)
let size1 = CGSize(width: 100, height: 100)
let rect = CGRectMake(100, 100, 100, 100)
let rect1 = CGRect(x: 0, y: 0, width: 0, height: 0)
3. 结构体构造器(构造函数/构造方法)
- Swift中的结构体和类跟其它面向对象语言一样都有构造函数, 而OC是没有的
- Swift要求实例化一个结构体或类的时候,所有的成员变量都必须有初始值,
- 构造函数的意义就是用于初始化所有成员变量的, 而不是分配内存, 分配内存是系统帮我们做的.
- 如果结构体中的所有属性都有默认值, 可以使用
结构体名称()
实例化一个结构体 - 如果结构体中的属性没有默认值, 可以自定义构造器, 并在构造器中给所有的属性赋值
- 其实结构体有一个默认的逐一构造器, 用于在初始化时给所有属性赋值
3.1 默认的结构体构造器(函数/方法)
struct Rect2 {
var width:Double
var height:Double = 0.0
}
//逐一构造器
var r2 = Rect2(width: 10.0, height: 10.0);
//错误写法, 顺序必须和结构体中成员的顺序一致
var r2 = Rect2(height: 10.0, width: 10.0);
//错误写法, 必须包含所有成员
var r2 = Rect2(height: 10.0);
3.2 扩充结构体的构造器(函数/方法)
- 1.默认情况下实例化结构体时,是在调用系统给结构体的提供的一个默认构造函数
- 2.在一个构造函数执行结束时,是必须保证所有的成员变量都已经被初始化
- 3.如果扩充了构造函数,并且没有明确的实现系统默认的构造函数,那么扩充的构造函数会覆盖系统默认的构造函数
struct Location {
var x : Double
var y : Double
// 由于上面定义结构体属性没有初始化, 所以这个是系统默认的构造函数
init(x : Double, y : Double) { // 明确实现系统默认的构造函数
self.x = x // 这里一定要写上 self , 区分传输参数与成员变量
self.y = y //
}
// 这里扩充了构造函数, 如果没有实现系统默认的构造函数,
// 在使用的时候也将没有系统默认的构造函数, 就只有扩充的构造函数
init(x : String, y : String) { // 扩充的构造函数
self.x = Double(x)!
self.y = Double(y)!
// 这里传入的参数, 转化之后是可选类型, 所以必须解包, 可能转化不成功或者为 nil ,
// 因此会导致程序崩溃, 所以最好做判断后再传解包的值, (这里没有写判断)
}
init() { // 提供通过 结构体名() 实例化出默认的结构体的函数
x = 0 //self.x = 0 // 这里可以不写 self
y = 0 //self.y = 0
}
}
let center = Location()
let center2 = Location(x: 10, y: 10)
let center3 = Location(x: "20", y: "30")
print(center)
print(center2)
print(center3)
// 输出结果:
//Location(x: 0.0, y: 0.0)
//Location(x: 10.0, y: 10.0)
//Location(x: 20.0, y: 30.0)
3.3 "值类型"的构造器代理
- 构造器代理: 构造方法之间的相互调用
- 构造方法可以调用其他构造方法来完成实例的构造, 称之为构造器代理
- 好处: 减少构造方法之间的重复代码
struct Rect1 {
var width:Double
var height:Double
init(width:Double, height:Double){
self.width = width
self.height = height
}
init(){
//width = 0
//height = 0
//构造器代理
self.init(width:0 , height:0)
}
func show(){
print("width = \(width) height = \(height)")
}
}
var r2 = Rect1()
r2.show()
//输出结果: width = 0.0 height = 0.0
var r3 = Rect1(width: 100, height: 100)
r3.show()
//输出结果: width = 100.0 height = 100.0
4. 结构体扩充函数(方法), 又称成员方法
- 在C和OC中结构体只有属性, 而Swift中结构体中还可以定义函数(方法)
4.1 给自定义的结构体扩充函数
- 给自定义的结构体扩充函数,必须在函数前加
mutating
- 给结构体定义一个函数, 该函数只属于该结构体
- 结构体中的成员函数必须使用某个实例调用
- 结构体内部函数可以访问成员属性
struct Location {
var x : Double
var y : Double
// 给结构体扩充函数,必须在函数前加 mutating
mutating func moveH(distance : Double) {
x += distance
}
mutating func moveV(distance : Double) {
y += distance
}
mutating func test() {
print("test")
}
}
var center = Location(x: 100, y: 100)
center.moveH(100)
center.moveV(-100)
print(center)
4.2 给系统的结构体扩充方法
- 必须在系统提供结构体类型前加上
extension
extension CGPoint {
mutating func moveH(distance : CGFloat) {
x += distance
}
}
var point = CGPoint(x: 100, y: 100)
point.moveH(50)
print(point)
4.3 (补充) 给系统的类扩充方法
- 使用系统提供的方法
let btn = UIButton()
btn.setTitle("按钮", forState: .Normal)
let title = btn.titleLabel!.text
- 给系统的提供的类添加方法, 必须在类名前加上
extension
关键字
extension UIButton {
func getTitle() -> String? {
return self.titleLabel!.text
}
}
let title1 = btn.getTitle()
5. 结构体是值类型
5.1 结构体变量或常量赋值是值拷贝
- 结构体是值类型, 结构体之间的赋值其实是将等号
=
右边的结构体中的值完全拷贝一份到等号=
左边的变量或常量 - 所以结构体间相互赋值是两个不同的实例, 是值拷贝拷贝
struct Rect4 {
var width:Double
var height:Double = 0.0
func show() -> Void{
print("width = \(width) height = \(height)")
}
}
var r5 = Rect4(width: 10.0, height: 10.0)
var r6 = r5
r5.show()
r6.show()
r5.width = 20.0
r5.show()
r6.show()
//输出结果:
//width = 10.0 height = 10.0
//width = 10.0 height = 10.0
//width = 20.0 height = 10.0
//width = 10.0 height = 10.0
5.2 结构体常量与存储属性的关系
-
结构体和枚举是值类型
- 因此不能修改结构体常量中的属性
- 不能修改结构体/枚举常量对象中的值, 因为他指向的对象是一个常量
struct Person2 {
var name: String
var age: Int
}
let p2: Person2 = Person2(name: "cdh", age: 20)
//因为结构体是值类型, 所以不能修改结构体常量中的属性
//不能修改结构体/枚举常量对象中的值, 因为他指向的对象是一个常量
//以下写法错误
//p2.name = "CDH" //不能修改结构体常量对象的值
//以下写法错误
//p2 = Person2(name: "CDH", age: 50)
5.3 (补充)类常量与存储属性的关系
-
类是引用类型
- 可以修改类常量中属性的值, 因为他指向的对象不是一个常量
class Person3 {
var name: String = "cdh"
var age: Int = 20
}
let p3:Person3 = Person3()
//可以修改类常量中属性的值, 因为他指向的对象不是一个常量
p3.name = "CDH"
//不可以修改类常量的指向
//以下写法是错误的
//p3 = Person4()