- 在Swift标准库中,绝大多数的公开类型都是结构体,而枚举和类只占很小一部分.
- 比如
Bool
Int
Double
String
Array
Dictionary
等常见类型都是结构体 - 所有的结构体都有一个编译器自动生成的初始化器(initializer,初始化方法,构造器,构造方法)
- 可以传入所有成员值,用以初始化所有成员(存储属性,Stored Property)
struct Date {
var year: Int
var month: Int
var day: Int
}
var date = Date(year: 2019, month: 6, day: 28)
结构体初始化器
- 编译器会根据情况,可能会为结构体生成多个初始化器,宗旨是:保证所有成员都有初始值.
struct Point {
var x: Int = 0
var y: Int = 0
}
let p1 = Point(x: 10, y: 10);
let p2 = Point(x: 10)
let p3 = Point(y: 9)
let p4 = Point()
struct Point {
var x: Int?
var y: Int?
}
var p1 = Point(x: 10, y: 1)
var p3 = Point(x: 4)
var p3 = Point(y: 4)
var p4 = Point()
以上代码可以在Xcode11
上编译通过.因为每个成员变量都有初始值nil
结构体内存
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
类
- 类的定义与结构体类似,但编译器并没有为类自动生成可以传入成员值的初始化器。
class Point {
var x: Int = 0
var y: Int = 0
}
let p4 = Point()
let p1 = Point(x: 10, y: 10);
let p2 = Point(x: 10)
let p3 = Point(y: 9)
image.png
image.png
类的初始化器
- 如果类的所有成员都在定义的时候指定了初始化知,编译器会为类生成无参的初始化器
- 成员的初始化是在这个初始化器中完成的
class Point {
var x: Int
var y: Int
init() {
x = 0
y = 0
}
}
let p4 = Point()
class Point {
var x: Int = 0
var y: Int = 0
}
let p4 = Point()
以上代码是完全等效的。
结构体与类的本质区别
- 结构体死是值类型(枚举也是值类型),类是引用类型(指针类型)
struct Point {
var x: Int = 3
var y: Int = 4
}
class Size {
var width = 1
var height = 2
}
func test() {
var size = Size()
var point = Point()
}
image.png
类的存储在堆空间,其中存储着类的 类型信息,引用计数 和成员变量的值。
值类型
- 值类型赋值给var ,let或者给函数传参,是直接将所有内容拷贝一份
- 类似对文件进行copy,paste操作,产生了全新的文件副本,属于深拷贝(depp copy)
struct Point {
var x: Int = 3
var y: Int = 4
}
func test() {
var p1 = Point(x: 10, y: 1)
var p2 = p1
}
image.png
p2.x = 99
p2.y = 777
//修改p2对p1没有影响,因为是两个完全独立的内存操作
值类型的赋值操作
- 在
Swift
标准库中,为了提升性能,String
Array
Dictionary
Set
采取了Copy On Write
的技术 - 比如仅当有
写
操作时,才会真正执行拷贝操作 - 对于标准库值类型的赋值操作,
Switf
能确保最佳性能,所以没必要为了保证最佳性能 来避免赋值 - 建议:不需要修改的,尽量定义成
let
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)//[1,2,3,4]
a1[0] = 2//[2,2,3]
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)//["max" : 10, "min" : 2,"Other" : 7]
print(d2)//["max" : 12, "min" : 2]
引用类型
- 引用赋值给
var
let
或者给函数传参,是将内存地址拷贝一份 - 类似与制作一个文件的替身(快捷方式,链接),指向的是同一个文件,属于浅拷贝(shallow copy)
class Point {
var x: Int = 0
var y: Int = 0
init(x: Int, y: Int) {
self.x = x
self.y = y
}
}
func test() {
let p1 = Point(x: 10, y: 22)
let p2 = p1
p2.x = 55
p2.y = 66
print(p1.x)//55
print(p1.y)//66
}
image.png
对象的堆空间申请过程
- 在Swift中,创建类的实例对象,要向堆空间申请内存,大概流程如下
class.__allocating_init()
libswiftCore.dyld:_swift_allocObjet_
libswiftCore.dyld:swift-slowAlloc
libsystem_malloc.dyld:malloc
- 在
Mac iOS
中的malloc
函数分配的内存大小总是16的倍数 - 通过
class_getInstanceSize
可以得知类的对象真正使用的内存大小
class Point {
var x = 11
var y = 22
var test = true
}
var p = Point()
class_getInstanceSize(type(of: p))//40
class_getInstanceSize(Point.self)//40
引用类型的赋值操作
class Size {
var width: Int
var height: Int
init(width: Int,height: Int) {
self.width = width
self.height = height
}
}
var s1 = Size(width: 10, height: 20)
s1 = Size(width: 33, height: 55)
image.png
值类型 引用类型的let
class Size {
var width: Int
var height: Int
init(width: Int,height: Int) {
self.width = width
self.height = height
}
}
let s1 = Size(width: 10, height: 20)
s1 = Size(width: 33, height: 55)//报错,s1被let修饰不能被修改
s1.width = 33
s1.width = 44
struct Point {
var x: Int
var y: Int
}
let p = Point(x: 10, y: 33)
p = Point(x: 00, y: 99)//报错,p被let修饰不能被修改
p.x = 77//报错,p被let修饰不能被修改
p.y = 77//报错,p被let修饰不能被修改
网友评论