Swift 结构体

对 Swift 学习 的一些总结
学习文献:
Chris Eidhof. “Swift 进阶”

类与结构体的主要不同点

  • 语义:

类:引用类型(引用语义),需要自己管理其引用计数、引用值得变化
结构体: 值类型(值语义),在设计结构体时,我们可以要求编译器保证不可变性。

  • 内存管理方式:

类:类的实例只能通过引用来间接地访问。类能有很多个持有者。
结构体:可以被直接持有及访问,不会被引用,但是会被复制。也就是说,结构体的持有者是唯一的。

  • 共享代码:

类: 通过继承来共享代码
结构体 (以及枚举):不能被继承。想要在不同的结构体或者枚举之间共享代码,我们需要使用不同的技术,比如像是组合、泛型以及协议扩展等。

值类型

值永远不会改变,它们具有不可变的特性。这 (在绝大多数情况下) 是一件好事,因为使用不变的数据可以让代码更容易被理解。不可变性也让代码天然地具有线程安全的特性,因为不能改变的东西是可以在线程之间安全地共享的。

值语义

  1. 结构体不能通过引用来进行比较,只能通过它们的属性来比较两个结构体。虽然可以用 var 来在结构体中声明可变的变量属性,但是这个可变性只体现在变量本身上,而不是指里面的值。改变一个结构体变量的属性,在概念上来说,和为整个变量赋值一个全新的结构体是等价的。我们总是使用一个新的结构体,并设置被改变的属性值,然后用它替代原来的结构体。
  2. 结构体只有一个持有者。比如,当我们将结构体变量传递给一个函数时,函数将接收到结构体的复制,它也只能改变它自己的这份复制。这叫做值语义 (value semantics) (又作复制语义)

引用语义

类对象会拥有很多持有者,这被叫做引用语义 (reference semantics)。

结构体

结构体为值类型

值类型意味着一个值变量被复制时,这个值本身也会被复制,而不只限于对这个值的引用的复制。在几乎所有的编程语言中,标量类型都是值类型。这意味着当一个值被赋给新的变量时,并不是传递引用,而是进行值的复制。

let a = 12
let b = a 
a += 1 
print (a) // 13
print (b) // 12
b的值没有根据a的值进行改变

结构体的赋值 特点

  1. 定义结构体 PY_Point 它里面有一个 let x = 12, 以及var y, 可以比较 y是否大于x
   struct PY_Point {
        let x = 12
        var y = 0
        func isReaterThanX() -> Bool {
            return y > x
        }
    }
  1. 赋值: 下面打印结果表示 两个结构体已经不是同一个结构体了。发生了复制现象。
       var point = PY_Point(y: 13)
        var pointTemp = point
    
        withUnsafePointer(to: &point) { pointAddress in
            print("point 地址: \(pointAddress)")
        }
        withUnsafePointer(to: &pointTemp) {pointTempAddress in
            print("pointTemp 地址: \(pointTempAddress)")
        }
        /**
         point 地址: 0x00007ffeec4d7a00
         pointTemp 地址: 0x00007ffeec4d7a10
         */
  1. 结构体(值类型)内部赋值:

可以看到,多次调用了didSet 方法,但是pointTemp的地址没有改变
“理解值类型的关键就是理解为什么这里会被调用。对结构体进行改变,在语义上来说,与重新为它进行赋值是相同的。当我们改变了结构体中某个深层次的属性时,其实还是意味着我们改变了结构体,所以 didSet 依然会被触发。
虽然语义上来说,我们将整个结构体替换为了新的结构体,但是编译器依然会原地进行变更。由于这个结构体没有其他所有者,实际上我们没有必要进行复制。不过如果有多个持有者的话,重新赋值意味着发生复制。对于写时复制的结构体,工作方式会略有不同

   var pointTemp: PY_Point? = PY_Point(y: 13) {
        didSet {
            print("pointTemp didSet 调用了")
        }
    }
 func valuation() {
    withUnsafePointer(to: &pointTemp) {pointTempAddress in
            print("pointTemp 地址: \(pointTempAddress)")
        }
        pointTemp?.y = -1
        withUnsafePointer(to: &pointTemp) {pointTempAddress in
            print("pointTemp 地址: \(pointTempAddress)")
        }
    }
//调用valuation()打印结果为 
/**
pointTemp 地址: 0x00007ffee9379a00
pointTemp didSet 调用了
pointTemp didSet 调用了
pointTemp 地址: 0x00007ffee9379a00
pointTemp didSet 调用了
*/

结构体内建方法

  1. 结构体可以添加方法 ,比如比较x,y大小的方法
struct PY_Point {
        let x = 12
        var y = 0
        func isReaterThanX() -> Bool {
            return y > x
        }
    }
  1. 结构体方法中修改自身的属性值

编译器自动补充了关键词 mutating
对不可变x属性,提示了Left side of mutating operator isn't mutable: 'x' is a 'let' constant (x为let 修饰,值不能被修改)

extension PY_Point {
    mutating func y_offset1() {
        y += 1
    }

    mutating func x_offset1() {
///报错 Left side of mutating operator isn't mutable: 'x' is a 'let' constant
        x += 1
    }
}

mutating / inout关键词

  • inout: 函数中,我们可以将参数标记为 inout 来使其可变。就和一个普通的参数一样,值被复制并作为参数被传到函数内。不过,我们可以改变这个复制 (就好像它是被 var 定义的一样)。然后当函数返回时,原来的值将被覆盖掉。
postfix func ++ (left: inout CGFloat) {
    left += 1
}
 var a:CGFloat = 1
        a++ 
        print(a)
// 打印结果为 2.0
  • mutating: 函数体内部你可以随时使用 self。如果我们想要改变 self,或是改变 self 自身或者嵌套的 (比如 self.y) 任何属性,我们就需要将方法标记为 mutating.mutating 将隐式的 self 参数变为可变的,并且改变了这个变量的值。
  • mutating 其实和inout做了同样的事情,它将隐式的 self 参数变为可变的,并且改变了这个变量的值

写时复制

在 Swift 标准库中, Array、Dictionary、 Set 这样的集合类型是通过一种叫做写时复制 (copy-on-write) 的技术实现的。
请参考 Swift 写时复制

你可能感兴趣的:(Swift 结构体)