Swift 初阶 —— inout 参数 & 数据独占问题

一、前言

我们都知道, 在 swift 里 inout 参数是用来修改实参的. 但其实 inout 参数的使用是要遵循"数据独占性"的, 要不然编译器会报错或警告.

inout 参数实际上只把对象的地址传了进来.

二、数据独占性

1、什么是数据独占

简单来说, "数据独占" 就是在单线程环境下, 当你往一块连续的内存写东西的同时, 不能读这块内存且不能同时往这块内存写其他东西.

2、为什么只有值类型才会有数据独占, 引用类型没有

这就涉及到 Swift 的设计哲学了. 因为一个引用类型的对象是一个指针而已, 且这个指针指向的实例是在堆上创建的, 所以这个堆上的实例是允许同时被多个指针指着的, 所以这个堆上的实例天生被设计出来就是为了共享的, 因此就引用类型没有数据独占这一说了.

但一个值类型的对象就是一块在栈上的连续内存, 不是指针. 所以值类型对象天生的设计就是不被共享的, 所以 Swift 才会在传 inout 参数时对值类型的对象实施数据独占.

3、访问重叠

3.1. 什么是访问重叠

访问重叠就是给多个 inout 参数传同一块连续内存. 这样做会导致 Swift 认为你在这个函数里同时对这块连续内存多次写入, 或边写边读, 于是 Swift 就认为你在破坏数据独占.

3.2. 访问重叠经典例子

3.2.1. 向多个 inout 参数传同一块连续内存

a. 同一个变量

b. 同一个数组(因为数组是内存连续的)

c. 同一个 struct 对象
图1 图 1 报错原因:

因为 state 传的是整块 appState 的内存, 而 max 传的是 appState 里的 limit 那块内存, 这两块内存是重叠了的, 所以 Swift 会怀疑你会对重叠的那部份内存边读边写, 于是就会报访问重叠 (overlapping accesses) 的错误.
图2 图 2 没报错原因:

这其实是 Swift6 的优化. appState 的 counter 和 appState 的 limit 这两块内存是不重叠的, 因此把这两块内存传到 value 和 max, 不会引起访问重叠的错误. 但这只是对 struct 的优化, 并不是对所有值类型对象的优化.

3.2.2. mutating 方法里使用逃逸闭包, 并在逃逸闭包里向 inout 参数写入

图 3 图 3 报错原因:

  1. inout 参数实际上只把对象的地址传了进来.
  2. mutating 方法实际会把 self 以 inout 的形式传进来, 因此 mutating 方法会对 self 要有数据独占性. 而如果如图 3 所示在逃逸闭包里修改 self, Swift 会认为你会在调用其他 mutating 方法时执行这个闭包, 那么对其他 mutating 方法而言就做不到 "数据独占" 了 (因为其他 mutating 函数里可能会出现同时向同一块内存写入的情况).
    图 4 图 4 为正确解决方案:

当我们把 self 声明在捕获列表里后, swift 实际上会把 self 拷贝出一个只读的副本, 然后再让闭包捕获这个不可变的副本. 所以闭包读到的 self 和 mutating 捕获到的 self 就不是同一块内存了, 就保证了 mutating 函数的 self 的数据独占.

三、总结

所以综上所述, swift 对 inout 参数的规定就是: inout 参数只能在当前函数的作用域内访问, 同时不能访问重叠. 除此之外, mutating 函数、setter、didSet & willSet 实际上都会把 self 以 inout 的方式传进来, 因此不能再这些函数里建一个逃逸闭包, 然后再在这个逃逸闭包里向 self 写值.

相关推荐
宠..2 分钟前
VS Code SSH 远程连接 Ubuntu 并实现快速运行(C/C++示例)
java·运维·c语言·开发语言·c++·ubuntu·ssh
Omics Pro3 分钟前
免费!糖蛋白质组学数据分析
开发语言·深度学习·数据挖掘·数据分析·r语言·excel·知识图谱
枫叶林FYL5 分钟前
【强化学习】2 大规模并行强化学习中的耦合策略优化:受控多样性驱动的样本高效探索
开发语言·php
chao1898446 分钟前
基于MATLAB的音频信号AM调制与解调实现
开发语言·matlab·音视频
雨落在了我的手上9 分钟前
初识java(八):数组的定义与使用
java·开发语言
贵州晓智信息科技9 分钟前
曼德勃罗集的 Three.js 实现
开发语言·javascript·ecmascript
xiaoshuaishuai810 分钟前
C# CUDA 到 OpenCL 迁移
开发语言·windows·c#
恋猫de小郭10 分钟前
Flutter 3.44 发布啦,超级大版本更新!!!
android·flutter·ios
AI科技星10 分钟前
基于平行素数对等腰梯形网格拓扑的完备性证明哥德巴赫猜想1+1
c语言·开发语言·网络·量子计算·agi
聆风吟º11 分钟前
【C标准库】深入理解C语言 isdigit函数详解:判断字符是否为数字
c语言·开发语言·库函数·isdigit