美文网首页Swift
Swift -02:值类型-引用类型

Swift -02:值类型-引用类型

作者: MonKey_Money | 来源:发表于2020-12-09 09:05 被阅读0次

    1.延迟存储属性

    class Teacher {
        lazy var age:Int = 10
    }
    

    1.用lazy修饰的存储属性

    2.延迟存储属性必须有一个默认的初始值

    我们不给age赋默认值会报错,惰性属性必须具有初始化程序,如果我们修改为可选 lazy var age:Int?,还是报同样的错误

    3.延迟存储属性第一次访问时才会被赋值

    let t = Teacher()
    t.age = 30
    print("end")
    

    我们在第一次访问之前和之后访问t的内存情况


    image.png

    我们看一下,加lazy和不加lazy,Teacher的大小是不是相同

    print(class_getInstanceSize(Teacher.self))
    

    不加lazy的大小是24,加lazy的大小是32
    为什么加了lazy之后会增加8个字节呢
    看sil代码

    swiftc -emit-sil main.swift | xcrun swift-demangle >> ./main.sil 
    
    image.png

    我们看到lazy修饰的属性,是一个optional的变量
    我们第一次getter方式时


    image.png

    我们第一次获取属性的值时,Optional的值时none,经过bb2赋值后变成case是some值为10
    Optional的值多大呢
    我们可以通过下面的命令打印看出

    print(MemoryLayout<Optional<Int>>.size)
    print(MemoryLayout<Optional<Int>>.stride)
    

    size为9,stride为16,Optional<Int>的尺寸大小,在后面会有详细介绍。stride为16是因为字节对齐。

    4.延迟存储属性并不能保证线程安全

    image.png

    这就导致age被初始化两次

    5.延迟存储属性对实例对象大小的影响

    如果存储属性时Int,不是延迟存储属性,则占8字节,如果是延迟存储属性则占9字节,9因为不是8的倍数,可能会出现内存对齐分配的内存可能会更大。

    2.类型属性

    class Teacher {
        static var age:Int = 10
    }
    

    1.用static修饰的存储属性

    2.类型属性必须有一个默认值

    如果不给默认值


    image.png

    3.类型属性只会被初始化一次

    我们还是通过sil看


    image.png

    类型属性是用单利属性初始化的

    3.单利的正确写法

    1.OC的单利写法

    static OCClass*_ocObject = nil;
    + (instancetype)shareManager {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            _ocObject = [[OCClass alloc] init];
        });
        return _ocObject;
    }
    

    2.swift的单利写法

    class Teacher {
        static let sharedInstance:Teacher = Teacher()
        private init() {}
    }
    

    使用static let创建声明一个实例对象
    给当前init添加访问控制权限private

    4.结构体的初始化

    1.结构体不需要自定义初始化方法

    struct  Teacher {
        var age:Int
        var name:String  
    }
    class Student {
        var age:Int
        var name:String
        
    }
    

    在class中会报 Class 'Student' has no initializers,这是因为编译器在结构体中自动帮助我们合成初始化方法,也就意味我们可以直接这样调用

    var teach = Teacher(age: 12, name: "Hello")
    

    查看sil代码

    struct Teacher {
      @_hasStorage var age: Int { get set }
      @_hasStorage var name: String { get set }
      init(age: Int, name: String)
    }
    

    2.如果我们的属性有初始化值,系统会提供不同的默认初始化方法

    struct  Teacher {
        var age:Int = 18
        var name:String = "Hello"
    }
    
    image.png

    3.如果我们自定义初始化方法,系统就不会帮我们生成初始化方法

    struct  Teacher {
        var age:Int = 18
        var name:String = "Hello"
        init(age:Int,name:String) {
            self.age = age;
            self.name = name
        }
    }
    
    image.png

    5.结构体是值类型

    1.什么是类型

    func test()  {
        var age = 18
        var age2 = age
        age = 30
        age2 = 45
        print("age \(age) age2 \(age2)")
    }
    

    在age = 30打断点和print处打断点查看内存情况


    image.png

    直接修改地址内的值18(0x12)-->30(0x1e) 18(0x12)--->45(0x2d)
    用withUnsafeMutablePointer(to: &age){print(&0)}查看指针地址
    1.值类型,地址中存储的是值
    2.值类型传递的过程是传递的副本

    6.mutating和inout

    mutating

    如果我们定义一个stack,类型为struct,

    struct MyStack {
        var items = [Int]()
        func push(_ item:Int)  {
            items.append(item)
        }
        
    }
    

    报错 Cannot use mutating member on immutable value: 'self' is immutable
    我们暂且修改代码

    struct MyStack {
        var items = [Int]()
        func push(_ item:Int)  {
            print("end")
    
        }
        
    }
    

    查看sil


    image.png

    在push中我们只所以能够访问 items,是因为push内有一个默认的参数self,但是我们现在看到self是let,因为MyStack是值类型,let修饰后就不能改变MyStack的值了,所以在上面的items.append(item)就会报错,我们怎么解决这个问题呢?
    用mutating修饰

    struct MyStack {
        var items = [Int]()
       mutating func push(_ item:Int)  {
        items.append(item)
        }
    }
    

    查看sil经过mutabting修饰之后默认参数self就是var类型的


    image.png

    mutabting的实质是让函数的参数增加inout修饰

    2. inout

    func swapTwoNumber(a:Int,b: Int)  {
        let  temp = a
        a = b
        b = temp
    }
    

    这种写法报错 Cannot assign to value: 'a' is a 'let' constant
    函数中参数默认是let类型,我们无法进行修改


    image.png

    如果我们想修改默认传入的参数,我们使用inout进行修饰

    func swapTwoNumber(a:inout Int,b: Int)  {
    }
    

    我们看sil,看inout做了什么


    image.png

    经过inout修饰的是地址取值,并用var修饰。而没用inout修饰的,简单的复制并且是let类型

    7.结构体方法调用

    结构体中的方法调度是静态调度(编译,链接完成之后当前的函数地址就已经确定存放在了代码段)
    1.查看当前mach文件的符号表

    nm   mach文件路径
    
    image.png

    2.还原符号

    xcrun swift-demangle  s13SwiftProperty9MyTeacherV7teacheryyF
    
    image.png

    相关文章

      网友评论

        本文标题:Swift -02:值类型-引用类型

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