2021年5月8日星期六

Swift系列六

在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: &amp;p))print(MemoryLayout<point>.size)print(MemoryLayout<point>.stride)print(MemoryLayout<point>.alignment)/* 输出: 0x000000000000000a 0x0000000000000014 0x0000000000000001 17 24 8 */

因为存储属性xy各占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: &amp;size)) print("class-size对象的内存地址",Mems.ptr(ofRef: size)) print("class-size.width的内存地址",Mems.ptr(ofVal: &amp;size.width)) print("class-size.height的内存地址",Mems.ptr(ofVal: &amp;size.height)) var point = Point() print("struct-point对象的内存",Mems.memStr(ofVal: &amp;point)) print("struct-point的内存地址",Mems.ptr(ofVal: &amp;point)) print("struct-point.x的内存地址",Mems.ptr(ofVal: &amp;point.x)) print("struct-point.y的内存地址",Mems.ptr(ofVal: &amp;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/

全球速卖通: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

没有评论:

发表评论