【问题标题】:Why doesn't referencing outside of a function create retain cycle?为什么在函数外部引用不创建保留周期?
【发布时间】:2017-05-15 19:32:01
【问题描述】:

抱歉,但我知道这是一个非常愚蠢的问题,而且我已经有点“知道”答案,但我需要有人清楚地向我解释为什么答案是这样的。

最近,在对各种内存问题进行了一些噩梦般的调试之后,我对代码中的保留周期和内存泄漏变得有点着迷/偏执,所以将来我想将它们扼杀在萌芽状态。但是在阅读和学习了很多关于 ARC 和 Swift 中的保留循环之后,虽然它是有道理的,但我仍然没有足够的“直觉”或自然感觉,我没有信心我能找到一个,或者缺少一个,因为我正在编码。所以我开始变得有点偏执,我甚至在没有意识到的情况下用基本的东西创建了保留周期。

那么,考虑到这一点,为什么任何使用在其外部声明的变量的普通函数不创建保留循环?例如:

    class someClass {
        let a = "I'm letter a"
        let moreLetters = addLetters()
        func addLetters () -> String { 
            let newString = a + "bcdefg"
            return newString
        }
    }

在这种情况下,self.moreLetters 引用函数 addLetters,然后常量 self.a 是来自函数 addLetters 的引用。那么,如果我不捕获弱/无主的自我,这会创建一个保留周期吗?对我来说,这么简单的事情会导致问题似乎很荒谬……或者是吗?在嵌套函数中怎么样,像这样:

    func someFunction () -> String {
        let a = "I'm letter a"
        func addLetters () -> String { 
            let newString = a + "bcdefg"
            return newString
        }
        let moreLetters = addLetters()
        return moreLetters
    }

这也会创建一个保留周期吗? (是的,我知道这是一种执行简单任务的复杂方式;我只是以这段代码为例来说明我的观点)。

我是否变得超级偏执并且严重过度思考事情?

【问题讨论】:

  • let moreLetters = addLetters() 编译错误。
  • 在 Swift 中,字符串是值类型而不是引用类型,因此在您的示例中不会出现引用循环。你能用引用类型重新表述你的问题吗?

标签: ios swift memory-leaks retain-cycle


【解决方案1】:

首先,您需要了解基本的保留循环是如何形成的。当一个对象A同时强引用一个对象B时,就形成了一个retain循环。对象 B 也强烈引用对象 A。

让我们看看你的第一段代码。

class someClass {
    let a = "I'm letter a"
    let moreLetters = addLetters()
    func addLetters () -> String { 
        let newString = a + "bcdefg"
        return newString
    }
}

实际上,一个类本身永远不会创建保留循环,所以让我们添加一些代码来创建一个对象:

var obj = someClass()

首先,a 被初始化为“我是字母 a”。之后,通过调用方法addLetters 初始化moreLetters。方法返回后,moreLetters 被初始化为“I'm letter abcdefg”。到目前为止一切顺利。

现在我们将obj 设置为nil

obj = nil

如果形成了保留循环,obj 将不会被取消初始化。然而,实际上,obj 被取消初始化,因为没有任何东西拥有对obj 的强引用!

“等一下!”你说,“但是addLetters 方法仍然引用someClass,因为它里面有a!”嗯,其实addLetters已经回来了!因此,其中的一切都不再重要!另外addLetters属于obj,你已经设置为nil了!

现在让我们看看你的第二个代码:

func someFunction () -> String {
    let a = "I'm letter a"
    func addLetters () -> String { 
        let newString = a + "bcdefg"
        return newString
    }
    let moreLetters = addLetters()
    return moreLetters
}

不会形成保留循环,因为甚至没有引用类型!没有要创建的对象。您在第二个代码中所做的只是使用字符串,它们是值类型。即使有一个类,也不会形成保留循环,因为正如我所说,当您将obj 设置为nil 时,其中的所有方法都会“消失”,因为方法属于对象。


那些我必须写 [weak self] 或保留循环表单的闭包呢?

那些闭包是转义闭包。正常的闭包在返回后会被取消初始化。但是,转义闭包被某些对象保留,因此它们不会立即取消初始化。欲了解更多信息,请参阅Escaping Closures in Swift

【讨论】:

  • 非常感谢 - 尽管我忘记了字符串是值类型因此不会导致保留循环这一简单事实,但感谢您幽默地回答我的问题!
猜你喜欢
  • 1970-01-01
  • 2023-03-19
  • 1970-01-01
  • 2010-10-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-04
  • 1970-01-01
相关资源
最近更新 更多