iOS Swift No.25 - 内存安全3

第二十五章 内存安全

4. Conflicting Access to Properties (属性中的冲突访问)

类型如结构体,元组,和枚举都是有单个值组成的,就像结构体的属性或者元组的元素。因为他们是值类型,变动任何一处值将会改变整个结果值。这就意味着给属性的读取和写入访问需要请求对整个结果值的写入和读取访问。例如,给元组中的元素重叠写入访问将会产生冲突。

var playerInformation = (health: 10, energy: 20)
balance(&playerInformation.health, &playerInformation.energy)
// 错误: 同时访问元组中的两个元素会产生冲突

在上面的这个例子中,在元组的元素中调用balance(::)方法将会产生冲突,因为给playerInformation有一个重叠写入访问。也就是说playerInformation.healthplayerInformation.energy都是以输入输出型参数传入的,这也就意味着balance(_:_:)需要在点用期间对两个元素有一个写入访问。在该例中对playerInformation的读取是会有访问持续时间的重叠所以会产生一次冲突。顾名思义,playerInformation元组是内存中的一个位置,在相同时间内对其元素health和energy同时进行了访问所以这样就符合冲突产生的条件了。

下面的代码向我们展示了相同的错误出现在给存储为全局变量的结构体的属性里有重叠写入访问。

var holly = Player(name: "Holly", health: 10, energy: 10)
balance(&holly.health, &holly.energy)  
// 错误:同时访问存储为全局变量的结构体属性中的两个值 产生冲突。

在实践中,绝大多是给结构体属性的访问都会安全地重叠不会产生冲突。例如,如果将全局变量holly修改为局部变量,此时编译器可以证明重叠访问对存储在结构体里的属性是安全的。

func someFunction() {
    var oscar = Player(name: "Oscar", health: 10, energy: 10)
    balance(&oscar.health, &oscar.energy)  // OK
}

在上面的例子中。Oscar的health和energy作为两个输入输出型参数传入给balance(::)的,所以编译器可以证明内存安全被保留,因为两个存储型参数无论如何都不可能交互。

对结构体属性的重叠访问的限制并不总是需要保持内存安全所必需的,内存安全是所需的保证,但独有访问是一个要比内存访问更加严格,这就意味着有些代码会保留内存安全,即便是它违反了内存的独有访问。如果编译器能够证明对内存的非独有访问仍然安全,Swift 允许此内存安全代码。特别是,如果能够证明重叠访问对结构体的属性是安全的,将会引用下面这些条件。

  1. 访问的只能是实例的存储型属性,不能是计算型属性或者类型属性。
  2. 结构体只能是局部变量值,不能是全局变量的值。
  3. 结构体要么不是被任何闭包捕获的,要么就是被非逃逸闭包捕获的

如果编译器不能证明这些访问是安全的,那么它将不会允许这些访问发生。

猜你喜欢

转载自blog.csdn.net/weixin_45026183/article/details/108005479