06-结构体和类
struct Date {
var year: Int
var month: Int
var day: Int
}
var date = Date(year: 2020, month: 6, day: 5)
- 所有的结构体都有一个编译器自动生成的初始化器(initializer,初始化方法、构造器、构造方法)
- 在第6行调用的,可以传入所有成员值,用以初始化所有成员(存储属性,Stored Property)
-
结构体的初始化器
- 编译器根据情况,可能会为结构体生成多个初始化器。前提是保证 所有成员都有初始值
struct Point {
var x: Int?
var y: Int?
}
var p1 = Point(x: 10, y: 10)
var p2 = Point(x: 10)
var p3 = Point(y: 10)
var p4 = Point()
struct Point {
var x: Int = 0
var y: Int = 0
}
var p = Point()
struct Point {
var x: Int
var y: Int
init() {
x = 0
y = 0
}
}
var p = Point()
struct Point {
var x: Int = 0
var y: Int = 0
var origin: Bool = false
}
print(MemoryLayout<Point>.size) // 17
print(MemoryLayout<Point>.stride) // 24
print(MemoryLayout<Point>.alignment) // 8
64位系统下,结构体中 x 占 8 个字节,y 占 8 个字节,origin 占 1 个字节,所以 Point 用到的内存一共占 17 个字节,但是因为要遵守内存对齐原则(8个字节),所以系统会分配 24 个字节来存储 Point。
-
类
-
类的定义和结构体类似,但编译器并没有为类自动生成可以传入成员值的初始化器
-
类的初始化器
-
如果类的所有成员都在定义的时候指定了初始值,编译器会为类生成无参的初始化器
-
成员的初始化是在这个初始化器中完成的
class Point {
var x: Int = 10
var y: Int = 20
}
let p1 = Point()
class Point {
var x: Int
var y: Int
init() {
x = 10
y = 20
}
}
let p1 = Point()
上面2段代码是完全等效的
class Size {
var width = 1
var height = 2
}
struct Point {
var x = 3
var y = 4
}
func test() {
var size = Size()
var point = Point()
}
上图都是针对 64 bit 环境。
struct Point {
var x: Int
var y: Int
}
func test() {
var p1 = Point(x: 10, y: 20)
var p2 = p1
}
struct Point {
var x: Int
var y: Int
}
func test() {
let p1 = Point(x: 10, y: 20)
var p2 = p1
}
请问 p1.x 和 p1.y 是多少?
p2.x = 11
p2.y = 22
因为 p2 是拷贝了 p1 的内容并且产生全新副本(深拷贝), 所以 p1 的值还是原来的值,与 p2 是否有变动无关。
var s1 = "Jack"
var s2 = s1
s2.append("_Rose")
print(s1) // Jack print(s2) // Jack_Rose
var a1 = [1, 2, 3]
var a2 = a1
a2.append(4)
a1[0] = 2
print(a1) // [2, 2, 3]
print(a2) // [1, 2, 3, 4]
var d1 = ["max" : 10, "min" : 2]
var d2 = d1
d1["other"] = 7
d2["max"] = 12
print(d1) // ["other": 7, "max": 10, "min": 2]
print(d2) // ["max": 12, "min": 2]
- 在Swift标准库中,为了提升性能,String、Array、Dictionary、Set 采取了Copy On Write的技术, 比如仅当有“写”操作时,才会真正执行拷贝操作
- 对于标准库值类型的赋值操作,Swift 能确保最佳性能,所有没必要为了保证最佳性能来避免赋值
- 建议:不需要修改的,尽量定义成 let
struct Point {
var x: Int
var y: Int
}
var p1 = Point(x: 10, y: 20)
p1 = Point(x: 11, y: 22)
class Size {
var width: Int
var height: Int
init(width: Int, height: Int) {
self.width = width
self.height = height
}
}
func test() {
var s1 = Size(width: 10, height: 20)
var s2 = s1
}
请问s1.width和s1.height是多少?
s2.width = 11
s2.height = 22
是 11 和 22 ,因为 s1 和 s2 保存的内存地址相同,所以当去修改 s2 的值时,s1 的值也会改变,他们都指向堆空间中的同一块内存。
在Swift中,创建类的实例对象,要向堆空间申请内存,大概流程如下
- Class.__allocating_init()
- libswiftCore.dylib: swift_allocObject
- libswiftCore.dylib: swift_slowAlloc
- libsystem_malloc.dylib: malloc
- 在Mac、iOS中的 malloc 函数分配的内存大小总是16 的倍数
- 通过 class_getInstanceSize 可以得知:类的对象至少需要占用多少内存
class Point {
var x = 11
var test = true
var y = 22
}
var p = Point()
class_getInstanceSize(type(of: p)) // 40
class_getInstanceSize(Point.self) // 40
可以看到,打印的结果都是 40 个字节。
在类中,x 占 8 个字节,y 占 8 个字节,test 占 1 个字节,所以目前我们看到的有 17 个字节。
但是因为在内存中,类存储在堆空间中,它前面会有 8 个字节存放类型信息,8个字节存引用计数,再加上面的,加起来一共是 33 个字节
根据内存对齐原则(8 个字节),系统会分配 40 字节来存储 Point。
-
可以看到,当结构体为 let 时,无论是重新赋值还是去修改结构体中变量的值,编译器都会报错,而类只有重新赋值的时候编译器才会报错,修改类中变量的值不会报错。
-
原因是结构体存在栈空间中,假如去修改了它的值或者它的变量,都会改变结构体的内存数据,使用 let 的时候是不允许的。
-
而实例化一个类时,它的指针常量是存在栈空间,所以不能被重新赋值,但是,指针常量所指向堆空间的内存地址中,width 这个变量是可以被修改的。原因有两点:1.修改类的中的变量,不会影响他在栈空间中定义的常量。2.因为是变量,所以可以被修改。
-
嵌套类型
struct Poker {
enum Suit: Character {
case spades = "♠️", hearts = "❤️", diamornds = "♦️", clubs = "♣️"
}
enum Rank: Int {
case tow = 2, three, four, five, six, seven, eight, nine, ten
case jack, queen, king, ace
}
}
print(Poker.Suit.hearts.rawValue)
var suit = Poker.Suit.spades
suit = .diamornds
var rank = Poker.Rank.five
rank = .king
一般把定义在枚举,结构体,类内部的函数,叫做方法。
enum PokerFace: Character {
case spades = "♠️", hearts = "❤️", diamornds = "♦️", clubs = "♣️"
func show() {
print("face is \(rawValue)")
}
}
let pf = PokerFace.diamornds
pf.show() // face is ♦️
struct Point {
var x = 10
var y = 10
func show() {
print("x = \(x), y = \(y)")
}
}
let p = Point()
p.show() // x = 10, y = 10
class Size {
var width = 10
var height = 10
func show() {
print("width = \(width), height = \(height)")
}
}
let s = Size()
s.show() // width = 10, height = 10
本文章只是本人的学习笔记!
上一篇: JavaScript(3)
下一篇: 12-初始化
推荐阅读
-
06-结构体和类
-
Java重写LinkedList方法详解,双向链表结构包括增删改查及List接口和测试类(不含迭代器)
-
浅谈Java中常用数据结构的实现类 Collection和Map
-
浅谈Java中常用数据结构的实现类 Collection和Map
-
写一个宏,计算结构体中某变量相对于首地址的偏移和写一个宏,可以将一个整数字的奇数位和偶数位交换
-
c#入门之枚举和结构体使用详解(控制台接收字符串以相反的方向输出)
-
Swift中结构体-Struct和类-Class的区别
-
swift 中类(class)和结构体(struct)区别
-
转:swift中结构体和类的区别(值类型和引用类型的区别)
-
Swift笔记结构体和类的区别