美文网首页
swift基础——类

swift基础——类

作者: 夜凉听风雨 | 来源:发表于2022-04-12 23:07 被阅读0次

类的定义和结构体类似,但编译器井没有为类自动生成可以传入成员值的初始化器

类.png

编译器只为类自动生成了一个无参的初始化器。

结构体.png

编译器为结构体自动生成了无参和有参的初始化器。

图片.png

如果没有初始值,类直接报错,编译器连无参的初始化器都不会为它自动生成。

初始化器

如果类的所有成员都在定义的时候指定了初始值,编译器会为类生成无参的初始化器
成员的初始化是在这个初始化器中完成的

图片.png

上面2段代码是相等的。

结构体与类的本质区别

结论:

结构体是值类型(枚举也是值类型) , 类是引用类型(指针类型)

图片.png

上图所示是一个结构体和类的对象内存分布。

变量point存储在栈空间,因为它是值类型,所以它的成员x,y都在栈空间中。
变量size也存储在栈空间,因为类是引用类型,所以它的内存中存储的是size对象的内存地址。而对象存储在堆空间,该对象占用32个字节。前8个字节存储的是指向类型信息的内存地址。第二个8字节存储的是指向引用计数的内存地址。最后16个字节存储的是成员width和height的值。

证明

使用工具Mems.swift来打印变量size和point的地址,以及它们的内容。 Mems.swift下载地址
代码如下:

    print("size变量的地址===", Mems.ptr(ofVal: &size))
    print("size变量的内容===", Mems.memStr(ofVal: &size))
    print("size所指向内存的地址===", Mems.ptr(ofRef: size))
    print("size所指向内存的内容===", Mems.memStr(ofRef: size))
    print("point变量的地址===", Mems.ptr(ofVal: &point))
    print("point变量的内容===", Mems.memStr(ofVal: &point))
    print("size变量占用空间===", MemoryLayout<Size>.stride)
    print("point变量占用空间===", MemoryLayout<Point>.stride)

打印结果:

size变量的地址=== 0x00007ffeefbff460
size变量的内容=== 0x00000001006667a0
size所指向内存的地址=== 0x00000001006667a0
size所指向内存的内容=== 0x000000010000c298 0x0000000200000003 0x0000000000000001 0x0000000000000002
point变量的地址=== 0x00007ffeefbff450
point变量的内容=== 0x0000000000000003 0x0000000000000004
size变量占用空间=== 8
point变量占用空间=== 16

通过打印结果可以发现,变量size和point的地址是紧挨在一起的,只相差了16个字节,这16个字节其实就是point所占用的空间。
point的内容分别是3和4,这也和我们的初始化的值吻合。
size的内容是一个地址,这个地址其实就是对象所在的堆区地址空间。
size指向的对象内容前2个8字节是两个内存地址,后第2个8字节分别存的是值3和4,和我们的初始化值吻合。
size变量占用空间是8个字节,因为它是一个指针类型,只存一个地址。
point变量占用空间是16个字节,因为它是一个值类型,会根据成员来开辟不同的空间。

值类型

  • 值类型赋值给var、let或者给函数传参,是直接将所有内容拷贝一份,类似于对文件进行copy、paste操作 ,产生了全新的文件副本。属于深拷贝( deep copy )

既然结构体是值类型,那么下面的代码p1和p2应该是两个完全不同的空间,并且更改p2的值,p1并不会被修改。

  struct Point {
        var x: Int
        var y: Int
    }
    
    var p1 = Point(x: 10, y: 20)
    var p2 = p1
    p2.x = 11
    p2.y = 22

查看汇编代码证明:

下面的汇编代码大致流程是调用Point.init方法,将10赋值给p1.x,20赋值给p1.y。然后将10赋值给p2.x,20赋值给p2.y。最后将12赋值给p2.x,22赋值给p2.y。

    0x1000035a1 <+1>:  movq   %rsp, %rbp
    0x1000035a4 <+4>:  subq   $0x20, %rsp
    0x1000035a8 <+8>:  xorps  %xmm0, %xmm0
    0x1000035ab <+11>: movaps %xmm0, -0x10(%rbp)
    0x1000035af <+15>: movaps %xmm0, -0x20(%rbp)
    0x1000035b3 <+19>: movl   $0xa, %edi // 把10给edi
    0x1000035b8 <+24>: movl   $0x14, %esi // 把20给esi
    0x1000035bd <+29>: callq  0x1000035f0 ; Point.init(x: Swift.Int, y: Swift.Int) -> Point in swiftTest.test() -> () at main.swift:34 
// 跳到下面的init方法里去
->  0x1000035c2 <+34>: movq   %rax, -0x10(%rbp)  // 把rax里的内容(10)给-0x10(%rbp) 也就是p1.x
    0x1000035c6 <+38>: movq   %rdx, -0x8(%rbp) // 把rdx里的内容(20)给-0x8(%rbp) 也就是p1.y
    0x1000035ca <+42>: movq   %rax, -0x20(%rbp) // 把rax里的内容(10)给-0x20(%rbp) 也就是p2.x
    0x1000035ce <+46>: movq   %rdx, -0x18(%rbp) // 把rdx里的内容(20)给-0x18(%rbp)也就是p2.y
    0x1000035d2 <+50>: movq   $0xb, -0x20(%rbp)  // 把12给-0x20(%rbp) 也就是p2.x
    0x1000035da <+58>: movq   $0x16, -0x18(%rbp) // 把22给-0x18(%rbp) 也就是p2.y
    0x1000035e2 <+66>: addq   $0x20, %rsp
    0x1000035e6 <+70>: popq   %rbp


// ------ init方法汇编代码-------
swiftTest`init(x:y:) in Point #1 in test():
    0x1000035f0 <+0>:  pushq  %rbp
->  0x1000035f1 <+1>:  movq   %rsp, %rbp
    0x1000035f4 <+4>:  movq   %rdi, %rax // 把rdi(上面的edi)里的内容(10)给rax
    0x1000035f7 <+7>:  movq   %rsi, %rdx // 把rsi(上面的esi)里的内容(20)给rdx
    0x1000035fa <+10>: popq   %rbp
    0x1000035fb <+11>: retq   

引用类型

引用赋值给var、let或者给函数传参,是将内存地址拷贝一份。类似于制作一个文件的替身(快捷方式、链接),指向的是同一个文件。属于浅拷贝( shallow copy ),修改替身的值也就相当于在修改原型的值。

图片.png

如上图所示,类是引用类型,所以s2=s1时就是浅拷贝,s1和s2内存空间里存储的都是Size(width:10, height:20)这个实例的地址。

汇编证明类是引用类型

运行如下代码:

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
    s2.width = 11
    s2.height = 22
}

test()

断点后查看汇编代码

调用allocating_init方法后在堆空间开辟地址存储实例,将返回的堆空间地址复制给了s1和s2两个变量的空间,所以s1和s2存储的都是同一块地址。

汇编1.png

查看allocating_init后rax寄存器存储地址对应的内存内容,可以看到就是实例的内存分布,有10和20两个数。

内存图.png

最后看汇编中修改s2.width和s2.height,其实修改的就是实例在堆空间中那个地址第16个字节和第32个字节。

汇编2.png

相关文章

  • Programming in Swift 编程指南

    Swift 基础篇 Swift 语言基础 Swift 中的字符串和集合 Swift 中的类 Swift 中的结构体...

  • Swift 基础(3) -- 根据类名创建对象

    1. 基础 Swift中的类名 在Swift中的类名是 : 命名空间 + 类名 .(比如:TestDemo.Per...

  • swift 基础方法的封装

    CTCateory swift oc 基本类用swift实现 前言 本文主要是针对swift封装基础方法类,方...

  • Swift中类的定义

    Swift中类的定义 Swift也是一门面向对象开发的语言 面向对象的基础是类,类产生了对象 在Swift中如何定...

  • Swift3.x - 类和结构体

    类的介绍和定义 Swift也是一门面向对象的开发语言。 面向对象的基础就是类,类产生对象。 Swift如何定义类:...

  • swift基础——类

    类的定义和结构体类似,但编译器井没有为类自动生成可以传入成员值的初始化器 编译器只为类自动生成了一个无参的初始化器...

  • 接上面的基础语法(Swift中类的使用)

    类的介绍和定义 1.Swift 也是面向对象的语言,面向对象的基础是类,类产生的对象,那么Swift中如何定义类呢...

  • 13-Swift中的类

    一、类的介绍和定义 Swift也是一门面向对象开发的语言,面向对象的基础是类,类产生了对象; 在Swift中定义类...

  • swift语言(swift3.0)基础知识(三)

    1、类的定义 1.1 Swift也是一门面向对象开发的语言。面向对象的基础是类,类产生了对象。 在Swift中如何...

  • 12-Swift中类的定义

    类的介绍 Swift也是一门面向对象开发的语言 面向对象的基础是类,类产生了对象 在Swift中如何定义类呢?cl...

网友评论

      本文标题:swift基础——类

      本文链接:https://www.haomeiwen.com/subject/onpwsrtx.html