Swift进阶(四)--- 值类型 & 引用类型

值类型:类似于本地的Excel,修改的内容只有自己知道。
引用类型:类似于在线的表格,修改的内容大家都知道。
在我们剖析值类型引用类型之前,我们向来回顾一下iOS的内存分区。

内存分区.png
  • 栈区的地址 比 堆区的地址大
  • 是从高地址->低地址,向下延伸,由系统自动管理,是一片连续的内存空间
  • 是从低地址->高地址,向上延伸,由程序员管理,的空间结构类似于链表,不是连续的
  • 日常开发中的内存溢出是指堆栈溢出,可以理解为栈区堆区边界碰撞的情况
  • 全局区常量区都存储在Mach-o中的_ _TEXT cString段t

接下里我们详细探讨一下值类型引用类型

值类型

我们来看下面的例子:

func text() {
    var size = 10
    
    var size_2 = size
    
    size = 20
    
    print("第一次改变")
    print("size=\(size), size_2=\(size_2)")
    
    siz_2 = 30
    print("第二次改变")
    print("size=\(size), size_2=\(size_2)")
}

text()
/************** 输出结果 **************/
第一次改变
size=20, size_2=10
第二次改变
size=20, size_2=30
Program ended with exit code: 0
  • 从输出结果来看,sizesize_2符合 本地Excel 的特点,两个对象各自修改自己的值,对方都不知情。初步判定:sizesize_2值类型

我们接着往下看,现在我们用LLDB来查看一下sizesize_2的内存结构

image.png

  • 从上图中,我们可以看出来,sizesize_2的内存地址符合栈内存的情况,是连续的(且是从高到底的),相差了8个字节。而这8个字节正好是一个Int

我们再来看一张图:


image1.png
  • 这一次我们总共设置了两个断点,通过两次LLDB调试,我们不仅能从内存地址的大小来断定sizesize_2是两个连续的内存地址,而且,从变化也可以清晰的分辨出来。

总结:值类型的特点:
1、值类型存储在
2、地址中存储的就是
3、值类型的传递过程中会产生新的副本,是深拷贝
4、值类型赋值给varlet或者给函数传参,是直接将所有内容拷贝一份

注意:

  • Swift标准库中,为了提升性能,StringArrayDictionarySet采取了Copy On Write的技术。
    • 比如仅当有 “写” 操作时,才会真正执行拷贝操作
    • 对于标准库值类型的赋值操作,Swift能确保最佳性能,所以没必要为了保证最佳性能来避免赋值
    • 注意:Copy On Write 只对Swift标准库起作用
  • 建议:不需要修改的,尽量定义成 let

结构体

  • 在Swift标准库中,绝大多数的公开类型都是结构体,而枚举和类只占很小一部分。
    • 比如:BoolIntDoubleStringArrayDictionary等常见的类型都是结构体
struct Date {
    var year: Int
    var month: Int
    var day: Int
}
var date = Date(year: 2020, month: 12, day: 17)
  • 所有的结构体都有一个编译器自动生成的初始化器(initializer、初始化方法、构造器、构造方法)
  • 编译器会根据情况,可能会为结构体生成多个初始化器,\color{red}{宗旨是:保证所有成员都有初始值}
    image2.png
  • 如果将结构体内的属性定义成可选类型会怎样呢?
    • 通过下图可以看到,编译器并不会报错
    • 因为可选项都有一个默认值nil,因此可以编译通过
      image3.png
  • 当然,我们也可以自定义初始化器。
    • 一旦在定义结构体时自定义了初始化器,编译器就不会再帮它自动生成其他初始化器,如下图所示:


      image4.png
  • 接下来我们通过汇编来窥探一下初始化器的本质
    • 下面给出两个结构体,通过汇编来看一下init
struct Date {
    var year: Int
    var month: Int

    init() {
        year = 2020
        month = 12
    }
}
var date = Date()
struct Date {
    var year: Int = 2020
    var month: Int = 12
}
var date = Date()

1、首先我们来看第一个结构体(进行断点调试):


image5.png
image6.png

2、我们再来看一下第二个结构体(进行断点调试):


image7.png
image8.png
image9.png
  • 通过对比上面连个结构体进入的init()方法,我们发现,最后进入的init()方法是一模一样的,没有任何区别。
  • 因此可以得出结论,结构体的初始化器无论是编译器默认的还是自定义的,其实没有本质区别。
  • 结构体的内存结构
struct Date {
    var year = 2020
    var month = 12
    var Good = true
}
print(MemoryLayout<Date>.size)
print(MemoryLayout<Date>.stride)
print((MemoryLayout<Date>.alignment))
/********** 输出结果 ************/
17
24
8
  • 当然,此时我们也可以用withUnsafePointer函数去查看结构体的内存地址。
  • 通过内存结构的查看,我们发现,结构体也是值类型

总结:
1、结构体值类型,且结构体的地址是第一个成员的内存地址(可通过LLDB查看)
2、结构体默认初始化器自定义初始化器没有任何区别
3、结构体一旦在定义结构体时自定义了初始化器,编译器就不会再帮它自动生成其他初始化器
4、所有的结构体都有一个编译器自动生成的初始化器
5、编译器会根据情况,可能会为结构体生成多个初始化器,宗旨是:保证所有成员都有初始值

引用类型

eg:

class Man {
    var age = 30
    var height: Int?
    
    
    init(_ age: Int) {
        self.age = age
    }
    
    init(height: Int) {
        self.height = height
    }
    
    init(_ age: Int, _ height: Int) {
        self.age = age
        self.height = height
    }
}

let M1 = Man.init(20)
let M2 = Man.init(height: 180)
let M3 = Man.init(18, 190)
  • 在类中,如果属性没有赋值,且为非可选项,此时需要自定义init方法

为什么类是引用类型?

eg:

class Man {
    var age = 30
    var height: Int?
}
let M1 = Man.init()

下面我们断点调试一下M1

image.png

  • 通过上图可以看到,M1里面存放的是地址。
    接下来我们通过值的修改来观察一下引用类型的变化(注意:上文中M1是let类型,接下来我们需要用var类型)
    image.png
  • 通过上图我们可以看到,M1M2里面存放的地址是一样的,因此var M2 = M1是地址拷贝,即浅拷贝。并且我们可以观察到M2.age的改变也影响到了M1.age的值。

接下来我们思考一个问题
\color{red}{如果结构体内存放类对象,会是什么情况?}
通过上文我们知道:

  • 结构体值类型
  • 引用类型
    那么结构体的结合会影响到对方的存储形式吗?
    image.png
  • 通过上图,我们可以看到p中的M1仍然存放的是地址,由此可见结构体中包含类对象并不会改变其存储形式。
  • 反过来也是一样的,包含结构体也不会形象结构体的存储形式,有兴趣的同学可以自己尝试一样,过程跟上面的一样。

总结:
1、引用类型
2、类对象里面存储的是地址,类对象之间的赋值属于地址传递(浅拷贝)
3、值类型引用类型互相包含的情况下,并不会改变各自的存储形式。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,125评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,293评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,054评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,077评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,096评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,062评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,988评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,817评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,266评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,486评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,646评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,375评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,974评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,621评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,642评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,538评论 2 352

推荐阅读更多精彩内容