认识Swift系列6之结构体和类

    // 1.结构体
    func test_struct() {
        // 在swift标准库中,绝大数公开的类都是结构体,枚举和类只占一小部分
        // Bool、Int、Double、String、Array、Dictionary等常见类型都是结构体
        
        struct Date {
            var year: Int
            var month: Int
            var day: Int
            
            var stringDesc: String {
                get {
                    return String(format: "%04d-%02d-%02d", year, month, day)
                }
            }
        }
        // 编译器会默认为结构体生成一个初始化器,参数包含所有的存储属性
        let date = Date(year: 2019, month: 7, day: 11)
        print("Date is \(date)")
        print("Datedesc is \(date.stringDesc)")
        
        // 编译器根据情况自动为结构体生成不同的的初始化器
        // 具体情况视成员属性而定,宗旨:所有存储属性创建出来都能被初始化
        struct Point1 {
            var x: Int
            var y: Int
            // 自动生成(保证x和y能被初始化)
            // Point1(x: <#T##Int#>, y: <#T##Int#>)
        }
        let p1 = Point1(x: 1, y: 1)
        
        struct Point2 {
            var x: Int = 0
            var y: Int
            // 自动生成(保证y能被初始化)
            // Point1(x: <#T##Int#>, y: <#T##Int#>)
            // Point2(x: <#T##Int#>)
        }
        let p21 = Point2(x: 2, y: 2)
        let p22 = Point2(y: 2)
        
        struct Point3 {
            var x: Int = 0
            var y: Int = 0
            // 自动生成(x和y都已经初始化了,不许要在保证,因此可以生成一下几个构造方法)
            // Point3()
            // Point3(x: <#T##Int#>, y: <#T##Int#>)
            // Point3(x: <#T##Int#>)
            // Point3(y: <#T##Int#>)
        }
        let p31 = Point3()
        let p32 = Point3(x: 2, y: 2)
        let p33 = Point3(x: 2)
        let p34 = Point3(y: 3)
        
        // 当自己定义了初始化函数后,编译器不会再自动生成其它初始化函数
        struct Point4 {
            var x: Int = 0
            var y: Int = 0
            
            // Point4(x: <#T##Int#>, y: <#T##Int#>)
            init(x: Int, y: Int) {
                self.x = x
                self.y = y
            }
            
            // 由于自定义了初始化器,以下三个构造器编译器不会生成
            // Point4()
            // Point4(x: <#T##Int#>)
            // Point4(y: <#T##Int#>)
        }
        let p41 = Point4(x: 2, y: 2)
        
        // 初始化成员值和构造器的本质
        /**
         事实上,虽然Test1中在定义成员的时候给了初始值,但a和b发生初始化的时机在init方法中
         即Test1中会自动生成类似Test2中的init方法,Test1和Test2完全等价
         */
        struct Test1 {
            var a: Int = 0
            var b: Int = 0
        }
        let t1 = Test1()
        struct Test2 {
            var a: Int
            var b: Int
            init() {
                self.a = 0
                self.b = 0
            }
        }
        let t2 = Test2()
    }
    test_struct()
    
    // 2.类
    func test_class() {
        // 类在初始化成员值时的原理和结构体相同,都是在无参init方法中
        // 默认情况下,编译器不会帮类生成初始化构造器
        /**
         class Size1 {
            var w: Int
            var h: Int
            // 注意:该类没有任何构造器
         }
         */
        
        /**
         class Size2 {
            var w = 10
            var h: Int
         }
         */
        
        // 当且仅当类的所有成员都有初始值的时候,编译器才会为类生成无参构造方法
        class Size3 {
            var w = 10
            var h = 20
        }
        let s3 = Size3()
        
        // 同结构体,一下两段代码完全等效[Size4 和 Size5 等效]
        class Size4 {
            var a: Int = 0
            var b: Int = 0
        }
        let s4 = Size4()
        class Size5 {
            var a: Int
            var b: Int
            init() {
                self.a = 0
                self.b = 0
            }
        }
        let s5 = Size5()
    }
    test_class()
    
    // 3.结构体和类的本质区别
    func test_struct_class() {
        // (1)结构和枚举:值类型
        // 创建对象,在哪里创建对象,就在哪里开辟对象需要的内存空间
        
        // (2)类:引用类型(指针类型)
        // 创建对象,对象需要的内存通常开辟堆空间,而在创建的地方使用一个指针指向该堆空间
        
        // 值类型
        struct sPoint1 {
            var x = 10
            var y = 30
        }
        let sp1 = sPoint1()
        /**
         sp1 的结构
         ┏━━━━━━━┓
         ┃ x(10) ┃
         ┃ y(30) ┃
         ┗━━━━━━━┛
         如果在函数内部创建sp1,则在栈空间开辟 16个字节
         如果在全局区创建sp1,则在数据段开辟 16个字节
         如果sp1是类的成员变量,则在堆空间开辟 16个字节
         */
        print("sPoint1 sp1 size:\(MemoryLayout.size(ofValue: sp1))")
        
        class cPoint1 {
            var x = 10
            var y = 30
        }
        let cp1 = cPoint1()
        /**
         cp1 的结构
         ┏━━━━━━━┓
         ┃ cp1   ┃     
         ┃(指针)  ------> 堆空间
         ┗━━━━━━━┛     ┏━━━━━━━━━━━━━┓
                       ┃ classInfo   ┃
                       ┃ retainCount ┃
                       ┃    x(10)    ┃
                       ┃    y(30)    ┃
                       ┗━━━━━━━━━━━━━┛
         这里,cp1是一个指针,同样,cp1可能在栈空间、堆空间、全局区
         但是 cp1指向的对象,一定在堆空间
         因此cp1占用8个字节,cp1指向的对象占用32个字节
         */
        print("cPoint1 cp1 size:\(MemoryLayout.size(ofValue: cp1))")
        print("cPoint1 cp1 obj size:\(class_getInstanceSize(type(of: cp1)))")
        
        // (3)值类型的赋值(实际上是深拷贝:Copy On Write)
        let sp2 = sp1 // 这里实际上会将sp1的内存拷贝一份到新的sp2空间
        var sp3 = sp1 // 同上
        // 不过在swift标准库中,为了提升性能,String、Array、Dictionary、Set等采取了Copy On Write技术
        // 该技术意思是,只有被拷贝的副本真正需要执行写(修改)操作的时候,才会进行深拷贝操作
        // 不过该操作只针对swift标准库类型
        // 所以在我们不需要修改类型时,尽量使用let定义,这样编译器直接不会进行深拷贝操作
        
        // 注意:
        var sp4 = sPoint1(x: 1, y: 2)
        sp4 = sPoint1(x: 3, y: 5)
        /**
         sp4 的结构
         ┏━━━━━━━━━━━━━━━━━┓         ┏━━━━━━━━━━━━━━━━━┓
         ┃0x1000000 x(1)   ┃ ------> ┃0x1000000 x(3)   ┃
         ┃0x1000008 y(2)   ┃         ┃0x1000008 y(5)   ┃
         ┗━━━━━━━━━━━━━━━━━┛         ┗━━━━━━━━━━━━━━━━━┛
         //该操作实际上类似于深拷贝,sp4不会开辟新空间,而是值覆盖
         */
        
        // (4)引用类型的赋值(浅拷贝,即拷贝内存地址:指针地址)
        let cp2 = cp1
        // 该操作只会让cp2拥有和cp1同样的指针地址
        // 即两个指针指向同一个对象,虽然cp2拥有cp1相关信息,但并不拥有独立空间
    }
    test_struct_class()
    
    
    // 4.let 对值类型和引用类型的区别
    func test_let_for_struct_class() {
        struct Point {
            var x = 1
            var y = 1
        }
        class Size {
            var x = 1
            var y = 1
            
            init(x: Int, y: Int) {
                self.x = x
                self.y = y
            }
        }
        
        let p = Point(x: 5, y: 4)
        // p.x = 8 // 报错
        
        let s = Size(x: 5, y: 4)
        s.x = 8
        /** 对比可知
         对于let修饰的:
           如果是值类型,那么该对象本身和内部成员都不可修改
           如果是引用类型,那么指针指向不可修改,但是指向的对象可以修改
         
         可以粗暴的这样理解
            对于值类型 p 被 let 修饰,由于p的成员都在let之后,则不可修改
            对于引用类型 s 被 let 修饰,s的在let之后,但s指向的对象却在另一个空间,不归let管
         */
    }
    test_let_for_struct_class()
    
    
    // 5.嵌套类型和方法
    func test_nest_func() {
        // 嵌套
        struct A {
            enum A1 {
                case a11, a12, a13, a14
            }
            enum A2: Int {
                case a21 = 2, a22, a23, a24
            }
        }
        var a1 = A.A1.a13
        a1 = .a14
        
        // 类、结构体、枚举 都可以定义方法
        class Size {
            var w = 10
            var h = 10
            
            func show(){
                print("Size w = \(w), h = \(h)")
            }
        }
        struct Point {
            var x = 10
            var y = 10
            
            func show(){
                print("Point w = \(x), h = \(y)")
            }
        }
        enum TestEnum {
            case a1, a2, a3
            func show(){
                print("A is \(self)")
            }
        }
        
        let s = Size()
        s.show()
        
        let p = Point()
        p.show()
        
        let e = TestEnum.a1
        e.show()
    }
    test_nest_func()
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,312评论 5 473
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,578评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,337评论 0 333
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,134评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,161评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,303评论 1 280
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,761评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,421评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,609评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,450评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,504评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,194评论 3 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,760评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,836评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,066评论 1 257
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,612评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,178评论 2 341

推荐阅读更多精彩内容