onevcat / OneV-s-Den-Comments

0 stars 0 forks source link

2018/11/defer/ #19

Open utterances-bot opened 3 years ago

utterances-bot commented 3 years ago

关于 Swift defer 的正确使用 | OneV's Den

其实这篇文章的缘起是由于在对 Kingfisher 做重构的时候,因为自己对 defer 的理解不够准确,导致了一个 bug。所以想藉由这篇文章探索一下 defer 这个关键字的一些 edge case。

https://onevcat.com/2018/11/defer/

BlueBd commented 3 years ago

你好,如下俩份代码结果不同,哪个符合预期呢?

func run() {
    var a = 1
    func add() -> Int {
        defer {
            a = a + 1
        }
        return a
    }
    a = add()
    print(a) /// a = 1
}

func run() {
    var a = 1
    func add() -> Int {
        defer {
            a = a + 1
        }
        return a
    }
    add()
    print(a) /// a = 2
}
onevcat commented 3 years ago

都符合预期,第一个打印的 aa = add() 重新赋值后的 a,而 add 返回的是 1。在返回后,a = a + 1 中左侧捕获的 a 已经不是 add 外面的 a 了。

而第二个例子打印的就是 var a = 1 时声明的 a

不过建议不要纠结这种东西...这个和 C 的 a++++a 有一拼了。正常人不会写这样的代码 😂

BlueBd commented 3 years ago

感谢回答! 是的,这个场景是基本不会出现的,做这个测试其实是为了验证一下defer的具体执行时机和对外部值引用的关系。文中描述的是不会持有外面的值,那么本质上还是同一个对象在执行指令,因此进行这个验证。 同时这个测试理论上先执行的return操作,在进行赋值之前,执行defer中的代码。通过断点可以验证确实是按照这个顺序执行的,不过不同的一点猜想是return之后的返回结果应该是被独立保存了,在之后执行defer代码的时候,a还是外面的对象,并且实际上是+1了,不过当defer结束之后,a应该才接收了被独立保存的返回值1,被重新赋值。

    var a = 1
    func run() {
        func add() -> Int {
            defer {
                self.a = self.a + 1
                /// step3.断点首次执行到这里 a = 2
                print(self.a)
            }
            /// step2.断点首次执行到这里 a = 1
            /// step4.断点第二次执行到这里 a = 2
            return a
        }
        /// step1.断点首次执行到这里 a = 1
        /// step5.断点第二次执行到这里 a = 2
        a = add()
        /// step6.断点执行到这里的时候a = 1,被赋值为之前保存的返回值
        print("run -- ~~~~~~~a-\(a)") /// a = 1
    }