Swift 值类型和引用类型

50 阅读7分钟

1. 一个简单的赋值语句

var num = 30
    
var num2 = num

打印内存地址

p withUnsafeMutablePointer(to: &num){print($0)}
0x000000016fc8f4a8

p withUnsafeMutablePointer(to: &num2){print($0)}
0x000000016fc8f4a0

x/4g 0x000000016fc8f4a8 // num
0x16fc8f4a8: 0x000000000000001e 0x0000000000000000
0x16fc8f4b8: 0x00000001ec28d000 0x00006000002708e0

x/4g 0x000000016fc8f4a0  // num2
0x16fc8f4a0: 0x000000000000001e 0x000000000000001e
0x16fc8f4b0: 0x0000000000000000 0x00000001ec28d000

p num = 8  // 修改num
x/4g 0x000000016fc8f4a0
0x16fc8f4a0: 0x000000000000001e 0x0000000000000008
0x16fc8f4b0: 0x0000000000000000 0x00000001ec28d000

可以看到 0x000000016fc8f4a0 和 0x000000016fc8f4a8 存储的值都是18,是分别存储的

2. 值类型 struct

struct AdModel {
    var title: String = "a"
    var num: Int = 10
}

var a = AdModel()
var b = a


(lldb) p a
(SnapKitDemo.AdModel)  (title = "a", num = 10)

(lldb) p b
(SnapKitDemo.AdModel)  (title = "a", num = 10)

// 打印 a 的地址
(lldb) p withUnsafeMutablePointer(to: &a) {print($0)}
0x000000016b52b488

// 打印 b 的地址
(lldb) p withUnsafeMutablePointer(to: &b) {print($0)}
0x000000016b52b470

// 读取 a 地址存储的
x/4g 0x000000016b52b488
0x16b52b488: 0x0000000000000061 0xe100000000000000
0x16b52b498: 0x000000000000000a 0x000000000000001e

// 读取 b 地址存储的
x/4g 0x000000016b52b470
0x16b52b470: 0x0000000000000061 0xe100000000000000
0x16b52b480: 0x000000000000000a 0x0000000000000061

0x000000000000000a == 10
0x0000000000000061 == "a"


// 修改a的num 并重新读a的内存地址
(lldb) p a.num = 16 
x/4g 0x000000016b52b488
0x16b52b488: 0x0000000000000061 0xe100000000000000
0x16b52b498: 0x0000000000000010 0x000000000000001e

3. 引用类型 class

class AdModel2 {
    var title: String = "a"
    var num: Int = 10
}

var c = AdModel2()
var d = c;

// 打印 c 和 d 发现变量存储的内存地址是一致的
(lldb) p c
(SnapKitDemo.AdModel2) 0x0000600000c798f0 (title = "a", num = 10)
(lldb) p d
(SnapKitDemo.AdModel2) 0x0000600000c798f0 (title = "a", num = 10)

// 打印c的地址
(lldb) p withUnsafeMutablePointer(to: &c) {print($0)}
0x000000016b52b468
// 打印d的地址
(lldb) p withUnsafeMutablePointer(to: &d) {print($0)}
0x000000016b52b460

// 读取c地址存储的值
(lldb) x/4g 0x000000016b52b468
0x16b52b468: 0x0000600000c798f0 0x0000000000000061
0x16b52b478: 0xe100000000000000 0x000000000000000a
// 读取d地址存储的值
(lldb) x/4g 0x000000016b52b460
0x16b52b460: 0x0000600000c798f0 0x0000600000c798f0
0x16b52b470: 0x0000000000000061 0xe100000000000000

// 可以看到c地址存储的值和d地址存储的值是一样的,和上面通过`p c`, `p d`是一样的

// 读取 0x0000600000c798f0
x/8g 0x0000600000c798f0
0x600000c798f0: 0x00000001049f9998 0x0000000200000003
0x600000c79900: 0x0000000000000061 0xe100000000000000
0x600000c79910: 0x000000000000000a 0x0000000000000000
0x600000c79920: 0x0000beadde6a9920 0x00000000000007fb

0x600000c79900: 存储的是 title: String = "a"
0x600000c79910: 存储的是 num: Int = 10

// 修改c.num == 3, 发现 c 和 d值的num 都被改成了 0x0000000000000003
p c.num = 3 
(lldb) p c
(SnapKitDemo.AdModel2) 0x0000600000c798f0 (title = "a", num = 3)
(lldb) p d
(SnapKitDemo.AdModel2) 0x0000600000c798f0 (title = "a", num = 3)

(lldb) x/8g 0x0000600000c798f0
0x600000c798f0: 0x00000001049f9998 0x0000000200000003
0x600000c79900: 0x0000000000000061 0xe100000000000000
0x600000c79910: 0x0000000000000003 0x0000000000000000
0x600000c79920: 0x0000beadde6a9920 0x00000000000007fb

4. Other

  1. 避免在struct中引用class
  2. mutating 关键字修饰的 func 会让func的默认参数self变成 inout类型的
struct Stack {
    var items = [Int]()
    mutating func push(obj: Int) {
        items.append(obj)
    }
}
// func 声明中默认参数是不可变,即用let 修饰的, 如果需要参数改变,可以加上 inout
// func push 没有添加mutating会报下面的错误
// Cannot use mutating member on immutable value: 'self' is immutable