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 写值.

相关推荐
Alair‎2 分钟前
103React数据处理
开发语言·前端·javascript
博语小屋3 分钟前
简单线程池实现(单例模式)
linux·开发语言·c++·单例模式
游戏开发爱好者810 分钟前
H5 混合应用加密 Web 资源暴露到 IPA 层防护的完整技术方案
android·前端·ios·小程序·uni-app·iphone·webview
墨雪不会编程13 分钟前
C++基础语法篇八 ——【类型转换、再探构造、友元】
java·开发语言·c++
老毛肚18 分钟前
登录架构设计
java·开发语言
南棱笑笑生19 分钟前
20251215给飞凌OK3588-C开发板适配Rockchip原厂的Buildroot【linux-6.1】系统时统计eth1的插拔次数
linux·c语言·开发语言·rockchip
消失的旧时光-194327 分钟前
Java 线程池(第四篇):ScheduledThreadPoolExecutor 原理与定时任务执行机制全解析
java·开发语言
dudke30 分钟前
js的reduce详解
开发语言·javascript·ecmascript
kevin_水滴石穿31 分钟前
docker-compose.yml案例
java·服务器·开发语言
coderxiaohan32 分钟前
【C++】用哈希表封装unordered_map和unordered_set
开发语言·c++·散列表