Swift

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

2020-12-09  本文已影响0人  MonKey_Money

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
上一篇 下一篇

猜你喜欢

热点阅读