JHHK

欢迎来到我的个人网站
行者常至 为者常成

5_1、结构体和类(一)

目录

结构体

在 Swift 标准库中,绝大多数的公开类型都是结构体,而枚举和类只占很小一部分
比如Bool、Int、Double、 String、Array、Dictionary等常见类型都是结构体

  struct Date {
      var year: Int
      var month: Int
      var day: Int
  }
  var date = Date(year: 2019, month: 6, day: 23)

所有的结构体都有一个编译器自动生成的初始化器(initializer,初始化方法、构造器、构造方法)
在第6行调用的,可以传入所有成员值,用以初始化所有成员(存储属性,Stored Property)

编译器会根据情况,可能会为结构体生成多个初始化器,宗旨是:保证所有成员都有初始值

一、系统初始化方法

func structPractice(){
    
    //1、系统自动生成的初始化方法
    do{
        print("----------1---------")
        struct Point {
            var x:Int
            var y:Int
        }
//        let p0 = Point()      //报错,没有该方法
//        let p1 = Point(x:1)   //报错,没有该方法
//        let p2 = Point(y:2)   //报错,没有该方法
        let p3 = Point(x:1,y:2) //系统只生成了该方法 Point(x: 1, y: 2)
        print(p3)
    }
    
    //2、系统自动生成的初始化方法
    do{
        print("----------2---------")
        struct Point {
            var x:Int = 0
            var y:Int = 0
        }
        
        let p = Point()
        print(p)//Point(x: 0, y: 0)

        let p1 = Point(x:1)
        print(p1)//Point(x: 1, y: 0)

        let p2 = Point(y:2)
        print(p2)//Point(x: 0, y: 2)
        
        let p3 = Point(x: 1, y: 2)
        print(p3)//Point(x: 1, y: 2)
    }
    
    
    //3、内存占用
    do{
        print("----------3---------")
        struct Point {
            var x:Int
            var y:Int
        }
        
        var p = Point(x: 1, y: 2)
        //内存占用
        print(MemoryLayout.size(ofValue: p))//16
        print(MemoryLayout.stride(ofValue: p))//16
        print(MemoryLayout.alignment(ofValue: p))//8
        print(Mems.ptr(ofVal: &p))     //变量地址 0x00007ffeefbff3b0
        print(Mems.memStr(ofVal: &p))  //变量内容 0x0000000000000001 0x0000000000000002
    }
}

二、自定义初始化方法

func structPractice2(){

    //1、自定义初始化方法
    //一旦在定义结构体时自定义了初始化器,编译器就不会再帮它自动生成其他初始化器
    do{
        print("------------1------------")
        struct Point {
            var x:Int
            var y:Int
            init(){
                x = 0
                y = 0
            }
            
            init(x:Int) {
                self.x = x
                self.y = 0
            }

            init(y:Int) {
                self.x = 0
                self.y = y
            }

            init(x:Int,y:Int) {
                self.x = x
                self.y = y
            }
        }
        
        let p = Point()
        print(p)//Point(x: 0, y: 0)
        
        let p1 = Point(x:1)
        print(p1)//Point(x: 1, y: 0)
        
        let p2 = Point(y:2)
        print(p2)//Point(x: 0, y: 2)
        
        let p3 = Point(x: 1, y: 2)
        print(p3)//Point(x: 1, y: 2)
    }
    
    
    //2、自定义初始化方法
    do{
        print("------------2------------")
        
        struct Point{
            var x:Int
            var y:Int
            init(x:Int=0,y:Int = 0){
                self.x = x
                self.y = y
            }
        }
        
        let p = Point()
        let p1 = Point(x:1)
        let p2 = Point(y:2)
        let p3 = Point(x:1,y:2)
        
        print(p,p1,p2,p3,separator:"\n")
    }
    
    

    //3、下面代码能编译通过吗
    do{
        print("------------4------------")
        
        //可选项默认值为nil,所以可以编译通过
        struct Point {
            var x:Int?
            var y:Int?
        }
        
        let p  = Point()       //可编译
        let p1 = Point(x:1)    //可编译
        let p2 = Point(y:2)    //可编译
        let p3 = Point(x: 1, y: 2)
        print(p,p1,p2,p3,separator:"\n")
    }
    
    
    //4、自定义初始化方法
    do{
        struct Point {
            var x:Int?
            var y:Int?
            init(x:Int=0,y:Int=0) {
                self.x = x
                self.y = y
            }
        }
    }
}

类的定义和结构体类似,但编译器并没有为类自动生成可以传入成员值的初始化器
如果类的所有成员都在定义的时候指定了初始值,编译器会为类生成无参的初始化器
成员的初始化是在这个初始化器中完成的

func classPractice() {
    
    //1、不会生成初始化方法
    do{
        //报错,没有初始化方法
//        class Point {
//            var x:Int
//            var y:Int
//        }
//        let p = Point()
    }

    
    //2、只会生成无参的初始化方法
    do{
        print("----------2-----------")
        class Point {
            var x:Int = 0
            var y:Int = 0
        }
        
        let p = Point()
//        let p1 = Point(x:1)
//        let p2 = Point(y:2)
//        let  p3 = Point(x:1,y:2)
        print(p.x,p.y)//0
    }
    
   
    
    //3、自定义初始化方法(等同于上边)
    do{
        print("----------3-----------")
        class Point {
            var x:Int
            var y:Int
            init() {
                x = 0
                y = 0
            }
        }
        let p = Point()
        print(p.x,p.y)
    }
    
    
    //4、自定义初始化方法
    do{
        print("----------4-----------")
        class Point {
            var x:Int
            var y:Int
            init(x:Int=0,y:Int=0) {
                self.x = x
                self.y = y
            }
        }
        let p = Point(x: 1, y: 2)
        print(p.x,p.y)
    }
}

结构体和类的本质区别

一、区别

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

img

func structAndClassCompare() {
    
    //1、结构体是值类型
    do{
        print("----------1---------")
        struct Point{
            var x = 10
            var y = 20
        }
        
        var p = Point()
        
        //p变量的地址 0x00007ffeefbff510
        //p变量的内容 0x000000000000000a 0x0000000000000014
        print("p变量的地址",Mems.ptr(ofVal: &p))
        print("p变量的内容",Mems.memStr(ofVal: &p))
    }
   
    
    //2、类是引用类型
    do{
        print("---------2-----------")
        class Size{
            var width = 10
            var height = 20
        }
        var s = Size()
        //s变量的地址 0x00007ffeefbff508
        //s变量的内容 0x0000000100545cf0
        //s所指向的内存地址 0x0000000100545cf0
        //s所指向的内存大小 32
        //s所指向的内存内容 0x000000010000ac88 0x0000000200000002 0x000000000000000a 0x0000000000000014
        print("s变量的地址",Mems.ptr(ofVal: &s))
        print("s变量的内容",Mems.memStr(ofVal: &s))
        print("s所指向的内存地址",Mems.ptr(ofRef: s))
        print("s所指向的内存大小",Mems.size(ofRef: s))
        print("s所指向的内存内容",Mems.memStr(ofRef: s))
    }
}

二、值类型

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

    struct Point {
        var x:Int
        var y:Int
    }
    
    func test (){
        let p1 = Point(x: 10, y: 20)
        let p2 = p1
    }

img

    var p1 = Point(x: 10, y: 20)
    p1 = Point(x: 11, y: 21)

img

三、引用类型

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

    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
    }

img

引用类型的赋值操作

img

四、结构体内存分析

func memoryAnalyForStruct() {
    //1、结构体内存分布
    struct Point{
        // 内存对齐如下:  偏移量   大小
        var x:Int   //    0      8
        var b1:Bool //    9      1
        var b2:Bool //    10     1
        var y:Int   //    16     8
    }
    
        var p = Point(x: 10, b1: true, b2: false, y: 20)
        print(MemoryLayout.size(ofValue: p))//24
        print(MemoryLayout.stride(ofValue: p))//24
        print(MemoryLayout.alignment(ofValue: p))//8
        print(Mems.ptr(ofVal: &p))//0x00007ffeefbff510
        print(Mems.memStr(ofVal: &p))//0x000000000000000a 0x0000000000000001 0x0000000000000014
    
    /**
     以上分析满足内存对齐条件:
     一、结构体变量中成员的偏移量必须是成员大小的整数倍(0被认为是任何数的整数倍)
     二、结构体大小必须是所有成员大小的整数倍。
     */
    
    
    
    //2、结构体的内存分布
    struct Point2 {
        var x:Int
        var b1:Bool
        var b2:Bool
        var y:Int
        init(x:Int,b1:Bool,b2:Bool,y:Int) {
            self.x = x
            self.b1 = b1
            self.b2 = b2
            self.y = y
        }
        func pointTest(){
            print("pointTest")
        }
    }
    
    
    var p2 = Point2(x: 10, b1: true, b2: true, y: 20)
    p2.pointTest()
    print(MemoryLayout.size(ofValue: p2))//24
    print(MemoryLayout.stride(ofValue: p2))//24
    print(MemoryLayout.alignment(ofValue: p2))//8
    print(Mems.ptr(ofVal: &p2))//0x00007ffeefbff510
    print(Mems.memStr(ofVal: &p2))//0x000000000000000a 0x0000000000000101 0x0000000000000014
    
    
    /**
     init 函数
     结构体内定义的 pointTest函数
     代码存在于 代码段 并不占用结构体的内存空间
     */
    print("end")
}

五、类的内存分析

func memoryAnalyForClass() {
    class Size{
        //类的前面有 16个字节 前8字节存储一个地址 指向类信息 第二个8字节存储引用计数
        var width:Int//8
        var b1:Bool//1
        var b2:Bool//1
        var height:Int//8
        
        init(width:Int,b1:Bool,b2:Bool,height:Int) {
            self.width = width
            self.b1 = b1
            self.b2 = b2
            self.height = height
        }
    }
    
    var s = Size(width: 10, b1: true, b2: true, height: 20)
    print(MemoryLayout.size(ofValue: s))//8
    print(MemoryLayout.stride(ofValue: s))//8
    print(MemoryLayout.alignment(ofValue: s))//8
    print(Mems.ptr(ofVal: &s))//0x00007ffeefbff520
    print(Mems.memStr(ofVal: &s))//0x0000000100514810
    
    print(class_getInstanceSize(Size.self))//40 内存对齐后 需要的空间
    print(class_getInstanceSize(type(of: s)))//作用同上
    print(Mems.size(ofRef: s))//48 malloc 堆空间的内存申请需要是16的倍数
    
    print(Mems.ptr(ofRef: s))//0x0000000100514810
    print(Mems.memStr(ofRef: s))//0x000000010000e060 0x0000000200000002 0x000000000000000a 0x0000000000000101 0x0000000000000014 0x0002000000000000
    
    print("end")
}

行者常至,为者常成!





R
Valine - A simple comment system based on Leancloud.