在Swift标准库中,绝大多数的公开类型都是结构体,而枚举和类只占很小一部分。
一、结构体
常见的Bool、Int、Double、String、Array、Dictionary等常见类型都是结构体。
自定义结构体:
struct Date { var year: Int; var month: Int; var day: Int;}var date = Date(year: 2019, month: 06, day: 02)
所有结构体都有一个编译器自动生成的初始化器(initializer、初始化方法、构造器、构造方法)。
Date(year: 2019, month: 06, day: 02)
传入的是所有成员值,用来初始化所有成员(叫做存储属性)。
1.1. 结构体的初始化器
编译器会根据情况,可能会为结构体生成多个初始化器,宗旨是:保证所有成员都有初始值。
从上面案例可以看出,编译器帮助生成初始化器的条件就是:让所有存储属性都有值。
> 思考:下面的代码能否编译通过?
可选项都有个默认值nil
,所以可以编译通过。
1.2. 自定义初始化器
一旦在定义结构体时自定义了初始化器,编译器就不会再帮它自动生成其他初始化器。
1.3. 探究结构体初始化器的本质
下面的两段代码是等效的:
代码一:
代码二:
经过对比发现,代码一和代码二的init
方法完全一样。也就是说,存储属性的初始化是在初始化构造方法中完成的。
1.4. 结构体的内存结构
struct Point { var x = 10 var y = 20 var b = true}var p = Point()print(Mems.memStr(ofVal: &p))print(MemoryLayout<point>.size)print(MemoryLayout<point>.stride)print(MemoryLayout<point>.alignment)/* 输出: 0x000000000000000a 0x0000000000000014 0x0000000000000001 17 24 8 */
因为存储属性x
和y
各占8个字节(连续内存地址),Bool
在内存中占用1个字节,所以Point
一共占用17个字节,由于内存对齐是8,所以一共分配了24个字节。
二、类
类的定义和结构体类似,但编译器并没有为类自动生成可以传入成员值的初始化器。
定义类:
如果存储属性没有初始值,无参的初始化器也不会自动生成:
如果把上面的类换成结构体(struct
)类型就不会报错:
2.1. 类的初始化器
如果类的所有成员都在定义的时候指定了初始值,编译器会为类生成无参的初始化器。
成员的初始化是在这个初始化器中完成的。
下面的两段代码是等效的:
代码一:
class Point { var x: Int = 0 var y: Int = 0}var p1 = Point()
代码二:
class Point { var x: Int var y: Int init() { self.x = 0 self.y = 0 }}var p1 = Point()
三、结构体与类的本质区别
结构体时值类型(枚举也是值类型),类是引用类型(指针类型)。
3.1. 内存分析结构体与类
示例代码:
class Size { var width: Int = 1 var height: Int = 2}struct Point { var x: Int = 3 var y: Int = 4}func test() { var size = Size() print("class-size对象的内存",Mems.memStr(ofRef: size)) print("class-size指针的内存地址",Mems.ptr(ofVal: &size)) print("class-size对象的内存地址",Mems.ptr(ofRef: size)) print("class-size.width的内存地址",Mems.ptr(ofVal: &size.width)) print("class-size.height的内存地址",Mems.ptr(ofVal: &size.height)) var point = Point() print("struct-point对象的内存",Mems.memStr(ofVal: &point)) print("struct-point的内存地址",Mems.ptr(ofVal: &point)) print("struct-point.x的内存地址",Mems.ptr(ofVal: &point.x)) print("struct-point.y的内存地址",Mems.ptr(ofVal: &point.y))}test()/* 输出: class-size对象的内存 0x00000001000092a8 0x0000000200000002 0x0000000000000001 0x0000000000000002 class-size指针的内存地址 0x00007ffeefbff4d0 class-size对象的内存地址 0x000000010061fe80 class-size.width的内存地址 0x000000010061fe90 class-size.height的内存地址 0x000000010061fe98 struct-point对象的内存 0x0000000000000003 0x0000000000000004 struct-point的内存地址 0x00007ffeefbff470 struct-point.x的内存地址 0x00007ffeefbff470 struct-point.y的内存地址 0x00007ffeefbff478 */
示例代码的在内存中:
经过分析可以看到,结构体的数据是直接存到栈空间的,类的实例是用指针指向堆空间的内存,指针在栈空间。上面示例代码中类的实例占用32个字节,其中前面16个字节分别存储指向类型信息和引用计数,后面16个字节才是真正用来存储数据的。而结构体占用的内存大小等于存储属性所占内存大小之和。
> 注意:在C
语言中,结构体是不能定义方法的,但是在C++
和Swift
中,可以在结构体和类中定义方法。在64bit环境中,指针占用8个字节。
>> 扩展:值类型(结构体、枚举)的内存根据所处的位置不同,内存的位置也不一样。例如,定义一个全局的结构体,内存在数据段(全局区)中;如果在函数中定义,内存存放在栈空间;如果在类中定义一个结构体,内存跟随对象在堆空间。
3.2. 汇编分析结构体与类
在Swift
中,创建类的实例对象,要向堆空间申请内存,大概流程如下:
Class.__allocating_init()
libswiftCore.dylib:_swift_allocObject_
libswiftCore.dylib:swift_slowAlloc
libsystem_malloc.dylib:malloc
在Mac,iOS中的malloc
函数分配的内存大小总是16的倍数(为了做内存优化)。
通过class_getInstanceSize
可以得知类的对象真正使用的内存大小。
import Foundationclass Point { var x: Int = 3 var y: Int = 4 var b: Bool = true}var p = Point()print(class_getInstanceSize(type(of: p)))print(class_getInstanceSize(Point.self))/* 输出: 40 40 */
内存占用大小 = 8(指向类型信息) + 8(引用计数) + 8(存储属性x) + 8(存储属性y) + 1(存储属性b) = 33;
内存分配大小 = 8(指向类型信息) + 8(引用计数) + 8(存储属性x) + 8(存储属性y) + Max(1(存储属性b), 8(内存对齐数)) = 40;
> 扩展:如果底层调用了alloc或malloc函数,说明该对象存在堆空间,否则就是在栈空间。
3.2.1. 汇编分析结构体
第一步:创建结构体,打断点进入汇编:
第二步:在callq...init()
函数处进入函数实现体(lldb进入函数体指令:si
):
结论:rbp
就是局部变量,所以结构体创建的对象是在栈中存储的。
> 扩展:一般情况下,rbp
就是局部变量,rip
是全局变量,ret
是函数返回。
3.2.2. 汇编分析类
第一步:创建结构体,打断点进入汇编:
第二步:在callq...__allocating_init()...
函数处打断点,进入函数体:
第三步:在callq...swift_allocObject
函数处打断点,进入函数体:
第四步:一直进入到libswiftCore.dylib swift_allocObject:
中,在callq...swift_slowAlloc
处打断点进入:
第五步:malloc
出现了,这时候继续进入函数体:
第六步:最终,对象是在libsystem_malloc.dylib
库中执行的malloc
:
经过上面分析,可以清晰的看到,对象是在堆空间存储的。
扩展:在Mac、iOS中,创建对象都是调用的
libsystem_malloc.dylib
动态库。
原文转载:http://www.shaoqun.com/a/729798.html
全球速卖通:https://www.ikjzd.com/w/81
友家速递:https://www.ikjzd.com/w/1341
在Swift标准库中,绝大多数的公开类型都是结构体,而枚举和类只占很小一部分。一、结构体常见的Bool、Int、Double、String、Array、Dictionary等常见类型都是结构体。自定义结构体:structDate{varyear:Int;varmonth:Int;varday:Int;}vardate=Date(year:2019,month:06,day:02)所有结构体都有一个
腾邦:https://www.ikjzd.com/w/1382
r标:https://www.ikjzd.com/w/1070
livingsocial:https://www.ikjzd.com/w/714.html
菜鸟网:https://www.ikjzd.com/w/1547
疫情天下事(2月22日):足不出户尽掌跨境电商要闻:https://www.ikjzd.com/home/116461
一艘中国集装箱船失控!撞上外轮致7个集装箱落入长江!:https://www.ikjzd.com/home/100387
没有评论:
发表评论