美文网首页
令人迷惑的Swift行为

令人迷惑的Swift行为

作者: 行知路 | 来源:发表于2020-09-07 10:32 被阅读0次

一、引言

        所有的程序员都知道,多线程可能导致程序发生逻辑错误。此类问题的一般原因是,对相同内存的访问导致脏数据。现在,语言、工具、库等的不断改善,大幅降低多线程导致的问题的可能性。如,在OC当中,GCD的出现,大幅降低了多线程操作的难度。但是,多线程问题依然会不时发生。
        虽然,这类问题不时发生,也不一定好排查、修改。但是,从理论角度上来说,我们对此种问题原理、本质是理解比较清楚的。
        但是,在Swift之中存在类似的问题,此类问题令人迷惑的行为。这种行为与上面所说的多线程导致脏数据的问题结果相同,但是又不像多线程问题那样让人明晰其背后的原理。

二、废多看码

// 这是一个函数,其有两个输入输出参数
// 此函数的功能也较简单:把输入参数求和后,把参数修改为和的一半
// 如果不明白inout是什么意思,请自行百度,或者查看笔者的相关swift语法文章
func balance(_ x: inout Int, _ y: inout Int) {
    let sum = x + y
    x = sum / 2
    y = sum - x
}

// 这里定义了一个元组
var playerInformation = (health: 10, energy: 20)

// 期望此行代码执行之后结果是
// playerInformation.health=15
// playerInformation.energy=15
balance(&playerInformation.health, &playerInformation.energy)

        请诸位读者先不要往下看,此时以自己的理解,来看看上面的代码是否有问题。

        以笔者对C、C++、OC等语言的理解,以及对Swift的理解,认为以上代码是没有什么问题的。但是,世事难料,调用balance函数的那一行语句居然导致运行时崩溃!!!


i报错截图
  • 右侧绿色框所标识的编译器的报错信息:同时访问地址0x0000000100002068,但是写访问需要是独占的。
  • 再看左侧两个红框所标识出来的,对地址0x0000000100002068的修改,他们都指向了调用balance的语句。
  • 0x0000000100002068就是playerInformation变量的地址

\color{#FF0000}{按照我们的理解,上述代码不会出错,也不应该出错!但,它就是出错了!}
\color{#FF0000}{------是不是让人很迷惑!!!------}

三、追根溯源

        上述代码抛出了一个令人迷惑的Swift行为。此问题是由Swift对于内存的访问控制策略导致的。

3.1 Swift内存访问控制

3.1.1 问题发生的前提

  • 读写(至少有一个写内存)内存
  • 涉及同一块内存
  • 读写同时发生

这里的同时,不是指多线程的同时(此种同时已经为大家所熟知),而是指单线程!

3.1.2 问题发生的表现

  • 运行时崩溃(如“废多看码”章节所示)
  • 编译期错误(如下图所示)


    image.png

3.1.2 内存问题出现的场景

  • 调用含有inout参数的方法(从进入方法一直到方法结束,都会对参数进行访问)
  • 调用有mutating修饰的方法(从进入方法一直到方法结束,都会隐含对self进行访问)

        以上方法操作的是值类型(结构体、元组)。


非值类型不会出现问题 值类型出现问题

对于值类型来说,访问其中的一个成员就会导致对整个实例的独占访问。

3.2 “废多看码”章节的解释

func balance(_ x: inout Int, _ y: inout Int) {
    let sum = x + y
    x = sum / 2
    y = sum - x
}

var playerInformation = (health: 10, energy: 20)

// 此语句输出playerInformation的地址
withUnsafePointer(to: &playerInformation) {ptr in print(ptr)}

balance(&playerInformation.health, &playerInformation.energy)

        对于上述代码,对balance函数的调用,我们进行分析。

  • 含有inout参数
  • 针对第一个参数,需要对playerInformation进行独占访问
  • 针对第二个参数,需要对playerInformation进行独占访问
  • 因为同时需要对playerInformation进行独占访问,所以出问题。
  • 满足了:同时(在balance函数内),访问同一块内存(playerInformation所代表的内存),且有写操作(其实两个都是对playerInformation进行写操作)

也许从读者角度来看,两次对playerInformation的独占访问,不应该出问题,但是Swift的编译器不这么看。

3.3 未完待续

        看了以上内容之后,不知道读者是明白了呢,还是更糊涂了,还是有些明白了。笔者看了之后,对这方面有了一些了解,所以就写了这篇文章。虽然笔者知道这是Swift编译器在背后做的一些工作,但是依然不是很理解这么做的原理。
        除了上述例子外,其实还有更令人疑惑的一些内容。

  • 当变量是全局变量时会出错


    全局变量出错
  • 当变量是局部变量不会出错


    局部变量不会出错

从应用角度来看,以上的理解已经可以了,碰到问题时不以至于十分惊讶。但是对于编译器在背后捣的鬼,还是希望能够有深入理解。如果哪位读者理解了,请不吝赐教!

        如果读者想看更多的资料,可以查看内存安全

相关文章

  • 令人迷惑的Swift行为

    一、引言 所有的程序员都知道,多线程可能导致程序发生逻辑错误。此类问题的一般原因是,对相同内存的访问导致脏数据。现...

  • 留光1

    (大型情感类节目。 (而且极其粗糙。糙到令人迷惑的程度。【迷惑行为大赏】 (学心理是不可能的了,想都不要想,即使要...

  • 迷惑行为??

    同性恋不恶心,腐男腐女恶心??? 我服了,你不觉得你自相矛盾吗? 什么是腐男腐女,就是支持同性恋,守护同性恋的男孩...

  • 迷惑行为

    从小到大,我最怕的就是夏天,原因只有一个,我会狂出汗。走着出汗,坐着出汗,安安静静呆着也出汗。 啊,这该死的温度让...

  • 迷惑行为

    这天辅导我们考研的李老师给我们找了个教室。 教室不是我们专业的,就让我们几个班委先去看看,占个座位,班长也跟着去了...

  • 迷惑行为

    迷惑行为,一直是我看他人时,不能理解的行为。例如:明明报了个班要学习,却只愿意一周学一次,不愿意复习,似乎学完是为...

  • 迷惑行为

    近期北京疫情复发,各地纷纷出台政策进行严控,紧张的气氛再次席卷而来,虽说疫情已控制住,但各地不敢有丝毫懈怠。值此全...

  • 迷惑行为

    喜欢律政片,喜欢看庭辩,甚至喜欢看合同里不同措辞和语序带来的微妙变化,但并不想正儿八经学法律。 喜欢看书评,喜欢看...

  • 迷惑行为

    这是某个知名作家写的:我爱我家门前的这颗枣子树! 老师说:“看看这!写得多好!用简洁明了的语言表达了作者是多么爱他...

  • 迷惑行为

    自己做的迷惑行为可能比谁都多啊,过于沉迷了我应该真的调整好自己的身体和观念以及生活习惯。这个方向目前已经固定了,所...

网友评论

      本文标题:令人迷惑的Swift行为

      本文链接:https://www.haomeiwen.com/subject/xzlkektx.html