关于 Swift 4 中内存安全访问

时间:2023-02-23 16:30:03

前言

本文主要翻译今年 The Swift Programming Language (Swift 4) 中新出的章节 -《Memory Safety》。在 Swift 4 中,内存安全访问进行很大的优化(《What's New in Swift 4 ?》)。

默认情况下,Swift 会克服代码层面上的一些不安全的行为,如:确保一个变量被初始化完后才能被访问、确保变量在销毁后不会被访问等等安全操作。

Swift 也会确保在多路访问内存中同一区域时不会冲突(独占访问该区域)。通常情况下,我们完全无需考虑内存访问冲突的问题,因为 Swift 是自动管理内存的。然而,在码代码的时候,了解那些地方可能发生内存访问冲突是非常重要的。通常情况下,如果你的代码有内存访问冲突,那么 Xcode 会提示编译错误或者运行时错误。

本文不会介绍什么是内存访问冲突。详见 The Swift Programming Language (Swift 4)。如果你写的是并发或者多线程的程序,内存冲突访问与单线程是非常相似的一个问题。本文主要讨论单线程上的内存冲突访问。如果想检测多线程是否存在内存访问冲突,你可以看看这篇文档

我们可以把访问分为两种:即时和长期(instantaneous & long-term)

  • 即时访问:即在访问开始至结束前都不可能有其他代码来访问同一区域。
  • 长期访问:即在访问开始至结束前可能有其他代码来访问同一区域。长期访问可能和其他即时访问或者长期访问重叠。

重叠访问主要带有 in-out 参数的函数(或方法)以及结构体中带有 mutating 关键字的方法。我们下面来看看例子。

In-Out 参数的访问冲突

一个函数对其 in-out 参数具有长期的访问权限,如下代码:

Excerpt From: Apple Inc. "The Swift Programming Language (Swift 4).” iBooks".


var stepSize = 1 func increment(_ number: inout Int) {
number += stepSize
} increment(&stepSize)
// Error: conflicting accesses to stepSize

在上述代码中,stepSize 是一个全局变量,而且被作为一个 in-out 参数传给 increment(_:) 方法。冲突的原因在于 numberstepSize 引用的是内存中同一区域,并且同时进行读写访问,因此导致访问冲突。

关于 Swift 4 中内存安全访问

我们可以采用复制 stepSize 的方式解决该问题:


// Make an explicit copy.
var copyOfStepSize = stepSize
increment(&copyOfStepSize) // Update the original.
stepSize = copyOfStepSize
// stepSize is now 2
// stepSize is now 2

self 的访问冲突

在结构体中,带有 mutating 关键字的方法调用期间对 self 具有写入权限。


extension Player {
mutating func shareHealth(with teammate: inout Player) {
balance(&teammate.health, &health)
}
} var oscar = Player(name: "Oscar", health: 10, energy: 10) var maria = Player(name: "Maria", health: 5, energy: 10) oscar.shareHealth(with: &maria) // OK

上述代码是 Ok 的,即时写入权限在时间上是重叠的,但是是分别访问 oscar 的 health 和 maria 的 health,因此在 shareHealth(with:) 方法中并没有发生内存访问冲突。

关于 Swift 4 中内存安全访问

然而,如果你把 oscar 作为参数传给 shareHealth(with:),那么就会产生内存访问冲突:


oscar.shareHealth(with: &oscar)
// Error: conflicting accesses to oscar

很显然,shareHealth(with:) 方法中的 selfteammate 同时指向内存中同一区域,即同时对 oscarhealth 进行读写访问,因此导致访问冲突。

关于 Swift 4 中内存安全访问

属性的访问冲突

像结构体、元组、枚举这些类型都是由各个值组成的,如:结构体的各种属性、元组的各种元素等等。因为它们都是值类型,这意味着对其中一个属性的读写访问就是对整个值进行读写访问。代码如下:


var playerInformation = (health: 10, energy: 20) balance(&playerInformation.health, &playerInformation.energy) // Error: conflicting access to properties of playerInformation

上述代码不难理解,因为元祖是值类型,上述 balance(_:_:) 发生内存访问冲突,即同时访问 playerInformation。

下面我们再看一下结构体,其中 holly 是一个全局变量


var holly = Player(name: "Holly", health: 10, energy: 10)
balance(&holly.health, &holly.energy) // Error

上述代码会报这样一个错误:Simultaneous accesses to 0x10****580, but modification requires exclusive access。其实就是内存访问冲突了,Swift 4 中也针对这块做了优化处理,感兴趣的同学可以查阅我之前写的一篇文章《[WWDC17] What's New in Swift 4 ?》

在实践中,上述代码中的 holly 一般是个局部变量而非全局变量,编译器可以保证对结构体的存储属性进行重叠访问是安全的,代码如下:


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

上述代码运行是 Ok 的,有时候,限制结构体的各属性进行重叠访问是没有必要的,这也就是为什么 someFunction() 没有发生冲突访问的原因。内存访问安全虽应当得到保证,但是独占访问比内存安全访问要求更加严格,从上述代码可看出,即时违背了独占访问的原则,内存安全也能得到保证。一般情况下,编译器会在如下条件下保证对结构体的存储属性进行安全的重叠访问:

  • 只访问某个实例的存储属性,而不是计算属性或类属性
  • 访问的是局部的结构体变量,而不是全局变量
  • 结构体没有被任何闭包所捕获,或者仅被非逃逸闭包捕获。

感兴趣的同学可以查阅这里 The Swift Programming Language (Swift 4)

关于 Swift 4 中内存安全访问的更多相关文章

  1. 浅谈 Swift 2 中的 Objective-C 指针

    浅谈 Swift 2 中的 Objective-C 指针 2015-09-07  499 文章目录 1. 在 Swift 中读 C 指针 2. 在 Swift 中创建 C 指针 3. 总结 作者:Ja ...

  2. java中内存分配策略及堆和栈的比较

    Java把内存分成两种,一种叫做栈内存,一种叫做堆内存 在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配.当在一段代码块中定义一个变量时,java就在栈中为这个变量分配内存空间 ...

  3. 深入理解Linux中内存管理

    前一段时间看了<深入理解Linux内核>对其中的内存管理部分花了不少时间,但是还是有很多问题不是很清楚,最近又花了一些时间复习了一下,在这里记录下自己的理解和对Linux中内存管理的一些看 ...

  4. C语言中内存对齐方式

    一.什么是对齐,以及为什么要对齐: 1. 现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定变量的时候经常在特定的内存地址访问, ...

  5. MySQL中内存分为全局内存和线程内存

    首先我们来看一个公式,MySQL中内存分为全局内存和线程内存两大部分(其实并不全部,只是影响比较大的 部分): 复制代码 代码如下: per_thread_buffers=(read_buffer_s ...

  6. IOS中内存管理机制浅解

    我们知道在程序运行过程中要创建大量的对象,和其他高级语言类似,在ObjC中对象时存储在堆中的,系统并不会自动释放堆中的内存(注意基本类型是 由系统自己管理的,放在栈上).如果一个对象创建并使用后没有得 ...

  7. C语言中内存分配 &lpar;转&rpar;

    在任何程序设计环境及语言中,内存管理都十分重要.在目前的计算机系统或嵌入式系统中,内存资源仍然是有限的.因此在程序设计中,有效地管理内存资源是程序员首先考虑的问题. 第1节主要介绍内存管理基本概念,重 ...

  8. LoadTest中内存和线程Troubleshooting实战

    LoadTest中内存和线程Troubleshooting实战 在端午节放假的三天中,我对正在开发的Service进行了LoadTest,尝试在增大压力的条件下发现问题. 该Service为独立进程的 ...

  9. Java虚拟中内存分块

    Java虚拟机JVM(Java Virtual Machine)中内存分块 JAVA中通常分为5个区域虚拟机栈.堆.方法区.程序计数器.本地方法区.我们一般讲的是Java虚拟机管理的四个区域虚拟机栈. ...

随机推荐

  1. WebServices&colon;WSDL的结构分析

    WSDL(Web Services Description Language,Web服务描述语言)是为描述Web Services发布的XML格式.W3C组织没有批准1.1版的WSDL,但是2.0版本 ...

  2. IDE显示无法打开源文件时解决方案

      解决方法:加上具体地址信息将 #include <qwidgetresizehandler_p.h> 改为 #include <C:\Users\INnoVation\Deskt ...

  3. Android 查看webview里面的图片

    今天介绍一下怎么查看WebView里面的图片,首先要设置WebView能够支持JavaScript,然后实现JavaScript的监听接口: mWebView.getSettings().setJav ...

  4. NOI 2014 感想

    NOI2014结束了,我卡线登上了领奖台... 这是我第一次NOI,我觉得我收获了很多东西: 1.考前心态不重要,重要的是实力 真正考试的时候是顾不得想其他事情的 2.测试数据是人出的!不是随机的!不 ...

  5. java Html2Image 实现html转图片功能

    //java Html2Image 实现html转图片功能 // html2image  HtmlImageGenerator imageGenerator = new HtmlImageGenera ...

  6. 移植一个开源点餐网到SAE平台上

    记得以前我准备弄个点餐网的,但是由于一些原因没有做下去. 前几天将网上的一个点餐源码移植到了SAE上,网址http://diancan4sae.sinaapp.com. 我想做个外卖网,先选一个学校周 ...

  7. AngularJS进阶&lpar;二十一&rpar;Angularjs中scope与rootscope区别及联系

    Angularjs中scope与rootscope区别及联系 scope是html和单个controller之间的桥梁,数据绑定就靠他了.rootscope是各个controller中scope的桥梁 ...

  8. MT【228】整数解的个数

    求方程$x+y+z=24$的整数解的个数,要求$1\le x\le 5,12\le y\le 18,-1\le z\le12$ 解:设$a_r$是方程$X+Y+Z=r$的满足上述要求的整数解的个数,那 ...

  9. 018&lowbar;nginx&lowbar;proxy死循环问题

    今天线上遇到一个请求一次,触发多次的请求,而且直接把nginx机器压垮了.经排查,经过如下: 一. server{ server www.jyall.com; location /latestrele ...

  10. opensuse个人初始化

    Auth: jin Date: 20140414 sourcedir=/mnt/shelldir=/home/shell/userdir= /home/jin#mount /dev/sdb1 /mnt ...