美文网首页Swift
Swift基础语法(五)类和结构体的简单认识

Swift基础语法(五)类和结构体的简单认识

作者: iOS之文一 | 来源:发表于2022-05-13 12:39 被阅读0次

    Swift基础语法文章汇总

    本文简述了结构体和类的基本定义,并且说明他们二者的区别,结构体是值类型,类是引用类型。区别于其他语言,Swift提供的标准库中大部分公开类型都是结构体,而非类。

    主要内容:

    1. 结构体
    2. 结构体和类的区别

    1、结构体

    在Swift标准库中,绝大多数的公开类型都是结构体,而枚举和类只占很小一部分

    1.1 常见结构体

    比如Bool、Int、Double、String、Array、Dictionary其实都是结构体类型。


    结构体类型.png

    1.2 结构体的初始化器

    初始化器实现:

    /*
     1、结构体的初始化器
     注意:初始化器必须保证所有存储属性都完成初始化
     */
    struct Point1 {
        var x: Int = 5
        var y: Int
    }
    
    //此时都有值
    var p11 = Point1(x: 10, y: 10)
    //xYou自己的初始值5
    var p12 = Point1(y: 10)
    //y没值
    //var p3 = Point(x:5)
    //x、y都没值
    //var p4 = Point()
    

    说明:

    1. 所有的结构体都有编译器自动生成的初始化器
    2. 并且编译器会根据存储属性的设置情况提供多个初始化器
    3. 怎样提供初始化器的宗旨是“保证所有成员都有值”

    本质:初始化器中给存储属性赋初始值
    代码:

    struct WYPoint {
        var x: Int = 0
        var y: Int = 0
    }
    var p = WYPoint()
    

    查看汇编

    1. 执行WYPoint结构体的初始化器
    调用初始化器.png
    1. 跳入函数内查看
    初始化器赋值.png

    说明:

    1. 可以看到对结构体进行初始化时,会调用init()初始化器
    2. 在init初始化器中,会给两个变量赋值,rbp就是结构体的地址值

    1.3 自定义结构体

    初始化器实现:

    /*
     2、自定义初始化器
     */
    struct Point2 {
        var x : Int = 0
        var y : Int = 0
        init(x: Int, y: Int) {
            self.x = x
            self.y = y
        }
    }
    //只有这个初始化器
    var p21 = Point2(x: 10, y: 10)
    //系统不再提供默认初始化器
    //var p22 = Point2(y: 10)
    //var p23 = Point2(x:5)
    //var p24 = Point2()
    

    说明:

    1. 注意初始化器的定义格式
    2. 我们看到如果提供了自定义的初始化器,默认的初始化器系统将不再提供

    1.4 结构体内存结构

    代码:

    /*
     3、结构体的内存查看
     */
    struct Point3 {
        var x: Int = 0
        var y: Int = 0
        var origin: Bool = false
    }
    print(MemoryLayout<Point3>.size) // 17
    print(MemoryLayout<Point3>.stride) // 24
    print(MemoryLayout<Point3>.alignment) // 8
    

    说明:

    1. .size得到的是结构体实际使用了内存大小
    2. .stride得到的是结构体的实际占用内存大小
    3. .alignment得到的是当前的对齐方式

    2、类

    类的定义和结构体是类似的,但是编译器并没有为类自动生成可以传入成员值的初始化器,只有一个空构造。
    类默认只有一个空的构造器,但是这个空构造中也必须给成员变量赋值
    虽然可以定义存储属性、计算属性、方法、下标,但是真正类的内存中存储的只有存储属性(可以理解为OC的成员变量)

    2.1 初始化器

    类的初始化器也必须要给所有的存储属性赋值

    代码:

    /*
     1、类的初始化器
     */
    //没有给存储属性赋值,类的定义会报错
    //class Point4 {
    //    var x: Int
    //    var y: Int
    //}
    //let p41 = Point4()//报错,没有给存储属性赋初始值
    
    //只有空构造
    class Point5 {
        var x: Int = 0
        var y: Int = 0
    }
    let p51 = Point5()//空构造中赋初始值,不报错
    //let p52 = Point5(x: 10,y: 20)//默认没有其他构造器
    //let p53 = Point5(x: 10)//默认没有其他构造器
    //let p54 = Point2(y: 20)//默认没有其他构造器
    

    说明:

    1. 系统只会给类提供空构造,而且空构造必须能够给类的存储属性赋初始值
    2. 在Point4类中,因为没有给x/y设置初始值,所以空构造也是没有的,因为不知道如何赋值
    3. 因此不在的定义中设置初始值,就必须自定义初始化器,而不能用空初始化器
    4. 在Point5类中,有初始值,此时就有空初始化器了
    5. 如果类的所有成员都在定义的时候指定了初始值,编译期会为类生成无参的初始化器
    6. 成员的初始化就在这个初始化器中完成的,虽然没写,但是系统会自动生成并帮我们实现

    注意:

    • 在类中的初始化值本质其实是通过构造器来赋值的
    • 类中只会自动生成不传参的空构造器

    2.2 反初始化器

    反初始化器可以理解为析构器(C++)
    代码;

    /*
     2、类的反初始化器
     */
    class Person {
        var x: Int = 0
        var y: Int = 0
        deinit {
            print("WY:Perosn对象销毁了")
        }
    }
    
    func testInit() -> Void {
        var person = Person()
    }
    
    testInit()//WY:Perosn对象销毁了
    
    //继承关系中的反初始化器
    class Student : Person {
        deinit {
            print("WY:Student对象销毁了")
        }
    }
    func testInit2() -> Void {
        var student = Student()
    }
    //WY:Student对象销毁了
    //WY:Perosn对象销毁了
    testInit2()
    

    说明:

    1. 定义方式和析构器一样,只是名字不一样
    2. 所用也是销毁对象
    3. deinit不接受任何参数,不能写(),没有返回值,不能自行调用,必须系统调用
    4. 父类的deinit能被子类继承,如果之类没有反初始化器,会直接调用父类的反初始化器
    5. 子类的deinit调用完毕后会调用父类的deinit

    2.3 类的内存分配和占用

    这里和OC是一样的,占用内存大小为8字节对齐,分配内存大小为16字节对齐
    详情可以查看我的另一篇博客苹果的内存对齐原理
    代码:

    //类的内存大小分析
    func testInstanceSize() {
        class Point {
            var x = 11//8
            var test = true//1
            var y = 22//8
        }//33、40、48
        
        let p = Point()
        print(class_getInstanceSize(type(of: p)))//40 print(class_getInstanceSize(Point.self))
        print(Mems.size(ofRef: p))//48
    }
    testInstanceSize()
    

    说明:

    1. 对象实际使用内存大小为33(8*2+8+1+8)(对象本身会有两个属性,分别是类信息和引用计数)
    2. 占用内存大小为8字节对齐,所以对象实际占用内存大小为40个字节
    3. 系统分配内存大小为16字节对齐,所以对象分配大小为48个字节

    3、区别

    本质区别在于结构体是值类型,类是引用类型。

    区别:

    1. 结构体是值类型,类是引用类型
    2. 类的实体即对象会存储在堆中,结构体的实例存储的地方取决于定义在哪里
      1. 比如定义在函数中,那么就在栈中
      2. 定义在类中,就在堆中
      3. 定义在全局区,那么就在全局区
      4. 结构体定义在类的方法中,也存储在函数栈中
    3. 结构体的拷贝默认是浅拷贝,对象的拷贝是深拷贝

    Swift的引用类型和值类型的区别在C++、OC、Java的概念都是一样的,有一定编码基础的同学肯定都接触过,便不在此赘述了

    注意:
    在Swift标准库中,为了提升性能,String、Array、Dictionary、Set当不出现写入修改时,是浅拷贝,如果是会出现写入的情况则是深拷贝

    • 比如如果是let赋值就不会深拷贝
    • 比如在编译时发现后续不会出现修改值的情况就不会深拷贝
    • 注意只有标准库才会这样的

    相关文章

      网友评论

        本文标题:Swift基础语法(五)类和结构体的简单认识

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