Swift 访问冲突(in-out)

简介

默认情况下,Swift可以防止代码中发生不安全的行为.例如,Swift确保变量在使用之前进行初始化,在取消分配后不访问内存,并检查数组索引是否存在越界错误。
Swift还确保对同一内存区域的多次访问不会发生冲突,因为需要修改内存中某个位置的代码才能对该内存进行独占访问。因为Swift自动管理内存,所以大多数时候你根本不需要考虑访问内存。但是,了解潜在冲突可能发生的位置非常重要,这样我们就可以避免编写对内存具有冲突访问权限的代码。如果你的代码确实包含冲突,那么你将收到编译时或运行时错误。

内存访问冲突

当我们设置变量的值或将参数传递给函数的操作时,会在代码中访问内存.

// A write access to the memory where one is stored.
var one = 1

// A read access from the memory where one is stored.
print("We're number \(one)!")

当代码的不同部分试图同时访问内存中的相同位置时,可能会发生冲突的内存访问。同时多次访问内存中的某个位置会产生不可预测或不一致的行为.在Swift中,有一些方法可以修改跨越多行代码的值,从而可以尝试在自己修改的过程中访问一个值。

如果您编写了并发或多线程代码,则对内存的冲突访问可能是一个熟悉的问题。但是,此处讨论的冲突访问可能发生在单个线程上,并且不涉及并发或多线程代码。

特征

-至少一个写操作
-访问内存中的相同位置
-他们的持续时间重叠

读写访问之间的区别:写访问权限会更改内存中的位置,但读访问权限则不会.内存中的位置指的是被访问的内容.例如,变量,常量或属性。存储器访问的持续时间是瞬时的或长期的。

如果在访问开始之后但在结束之前其他代码无法运行,则访问是即时
函数具有对其所有输入输出参数的长期写访问权。

例如,下面代码清单中的所有读写访问都是即时的

func oneMore(than number: Int) -> Int {
    return number + 1
}

var myNumber = 1
myNumber = oneMore(than: myNumber)
print(myNumber)
// Prints "2"

但是,有几种方法可以访问内存,称为长期访问,跨越其他代码的执行.即时访问和长期访问之间的区别在于,其他代码可以在长期访问开始之后但在结束之前运行,这称为重叠。长期访问可以与其他长期访问和即时访问重叠。

重叠访问主要出现在使用函数和方法中的in-out修饰的参数或mutating修饰的方法代码中。使用长期访问的特定Swift代码类型将在下面的部分中讨论。

in-out

in-out是修饰函数参数类型,表示该参数在函数内修改后(即函数返回后),其值为修改后的值.

1,适用类型为变量
2,in-out修饰后的参数,在传参时需&修饰

in-out 访问冲突

var stepSize = 1

func increment(_ number: inout Int) {
   number += stepSize
}

increment(&stepSize)
// Error: conflicting accesses to stepSize
in-out
解决
// Make an explicit copy.
var copyOfStepSize = stepSize
increment(&copyOfStepSize)

// Update the original.
stepSize = copyOfStepSize
// stepSize is now 2

方法内访问冲突

struct Player {
   var name: String
   var health: Int
   var energy: Int

   static let maxHealth = 10
   mutating func restoreHealth() {
       health = Player.maxHealth
   }
}

在上面的 restoreHealth()方法中,对 self的写访问权限从方法的开头开始,一直持续到方法返回为止.在这种情况下, restoreHealth()中没有其他代码可以重叠访问Player实例的属性。下面的 shareHealth(with :)方法将另一个 Player实例作为 in-out参数,从而创造了重叠访问的可能性。

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

在上面的例子中,为Oscar的玩家调用shareHealth(with :)方法与Maria的玩家分享健康状况不会引起冲突.在方法调用期间有一个对oscar的写访问权,因为oscar是变异方法中self的值,并且在相同的持续时间内对maria进行写访问,因为maria作为in-out参数传递。

屏幕快照 2019-06-26 下午5.56.48.png

但是,如果您将oscar作为参数传递给shareHealth(with:),则存在冲突:

oscar.shareHealth(with: &oscar)
// Error: conflicting accesses to oscar
屏幕快照 2019-06-26 下午6.19.09.png

属性访问冲突

结构,元组和枚举等类型由单个组成值组成,例如结构的属性或元组的元素.因为这些是值类型,所以改变值的任何部分都会改变整个值,这意味着对其中一个属性的读或写访问需要对整个值的读或写访问权限
例如,重叠对元组元素的写访问会产生冲突:

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

实际上,大多数对结构属性的访问都可以安全地重叠。
例如,如果上例中的变量holly更改为局部变量而不是全局变量,则编译器可以证明对结构的存储属性的重叠访问是安全的:

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

在上面的例子中,Oscar的health和energy 作为balance( :)输入参数。编译器可以证明保留了内存安全性,因为两个存储的属性不会以任何方式进行交互。
为了保持存储器安全性,并不总是必须限制对结构属性的重叠访问.内存安全是理想的保证,但独占访问是比内存安全更严格的要求 - 这意味着一些代码可以保持内存安全,即使它违反了对内存的独占访问权限.如果编译器能够证明对内存的非独占访问仍然是安全的,那么Swift允许这种内存安全的代码。具体而言,如果满足以下条件,则可以证明对结构属性的重叠访问是安全的:

只访问实例的存储属性,而不是计算属性或类属性。
结构是局部变量的值,而不是全局变量。
该结构要么不被任何闭包捕获,要么仅由non-escaping捕获。

参考资料

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容

  • 这是16年5月份编辑的一份比较杂乱适合自己观看的学习记录文档,今天18年5月份再次想写文章,发现简书还为我保存起的...
    Jenaral阅读 2,732评论 2 9
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,089评论 1 32
  • 默认情况下,Swift可以防止代码中出现不安全行为。例如,Swift确保变量在使用之前被初始化,内存在被释放后不被...
    WSJay阅读 1,685评论 1 7
  • 参考资源《swifter》https://github.com/iOS-Swift-Developers/Swif...
    柯浩然阅读 1,430评论 0 6
  • importUIKit classViewController:UITabBarController{ enumD...
    明哥_Young阅读 3,776评论 1 10