Swift4:为什么以及何时同时访问单独的struct元素是非法的?

时间:2018-10-28 08:03:44

标签: swift4

类似于 this query 中的情况,

  

我收到运行时错误 (Swift 4.2 Xcode 10.0)   在对象方法中尝试“ 同时访问”来对两个单独的对象进行访问   单个实例结构的成员。

如果我尝试对两个单独的实例成员(从概念上讲,也是一个不同的实例结构的两个单独成员:对象本身;即自我)进行相同类型的访问,则没有问题。谁能帮助我了解我所违反的状况以及其存在的原因?

import Cocoa

struct SomeStruct {
 var a:Int = 0
 var b:Int = 0
}

func writeTwoValues(first:inout Int, second:inout Int) {
 first = 1
 second = 2
}

@NSApplicationMain class AppDelegate: NSObject, NSApplicationDelegate {

 @IBOutlet weak var window: NSWindow!

 fileprivate var someStruct = SomeStruct() // Instance struct of ints

 var c:Int = 0  // Instance ints not inside a struct
 var d:Int = 0

 func applicationDidFinishLaunching(_ aNotification: Notification) {

      writeTwoValues(first: &c, second: &d)   // Works great

      writeTwoValues(first: &someStruct.a, second: &someStruct.b)
     // Fails at runtime with "Simultaneous accesses..., but modification requires exclusive access
 }
}

上面引用的问题的被接受的响应者写道,在这种访问方式下,独占访问的目的是“防止您确切地做您想做的事情”。但是 WWDC视频 SE-0176 仅包含示例,其中相同的内存被两个不同的引用作为别名。我了解为什么这些有问题。但是,在上面的代码中,就像在引用的查询中一样,这两个引用在所有运行时执行路径中都针对独立且不重叠的内存位置。我们永远不会同时对同一内存或内存的重叠部分(例如成员和包含它的结构)进行两次io访问。那么为什么这是设计和运行时错误?

(我理解,如果您可以访问结构定义和采用io参数的方法,那么重写这些示例很简单。如果您访问的是固定的API,那么您的选择就更少了。我对理解为什么更感兴趣Swift拒绝了此代码,法律代码的精确界限落在哪里,而不是弄清楚如何使我的示例在此处以不同的方式运行。)

1 个答案:

答案 0 :(得分:0)

正如您正确指出的那样,实际上在您的代码中没有冲突的访问。问题是Swift是否能够识别还是安全。

转向 Swift编程语言(Swift 4.2),它与我们在内存安全一章中找到的Swift的正式定义非常接近:

  

访问属性冲突

     

诸如结构,元组和枚举之类的类型由各个组成值组成,例如结构的属性或元组的元素。因为这些是值类型,所以对值的任何部分进行修改都会对整个值进行修改,这意味着对属性之一的读写访问要求对整个值的读写访问。

在这里您可以将“因为这些是值类型”读为“在Swift中,决定复合值类型”,即Apple做出选择并以这种方式定义事物,其他语言也可能做出其他选择。

因此,根据该声明,您的代码冲突。但是,几段之后,Apple撰写了有关放宽此规范的内容:

  

实际上,对结构属性的大多数访问都可以安全地重叠。

     

[与您的示例类似,但它使用局部变量]

     

编译器可以证明保留了内存安全性,因为这两个存储的属性不会以任何方式交互。

因此,苹果公司说在 local 变量的情况下,编译器可以确定没有重叠访问,并放宽对任何成员的访问都视为对整体的访问的限制。

但是您使用的是 class instance 变量。苹果在几段之后说:

  

具体来说,如果满足以下条件,则可以证明对结构属性的重叠访问是安全的:

     
      
  • 您仅访问实例的存储属性,而不访问计算的属性或类属性。

  •   
  • 结构是局部变量的值,而不是全局变量。

  •   
  • 该结构要么未被任何闭包捕获,要么仅被不冒号的闭包捕获。

  •   

从您的代码中我们可以说它看起来不是“不是全局变量”,而是除“局部变量”以外的其他任何含义。

当然我在这里说 ,因为我们都知道Swift是一个松散定义的移动目标,下周二它的编译器和语义可能会有所不同;-)

HTH