为什么在函数外部引用不创建保留周期?
Why doesn't referencing outside of a function create retain cycle?
抱歉,但我知道这是一个非常愚蠢的问题,我已经 'know' 找到了答案,但我需要有人向我清楚地解释为什么答案是这样。
最近,我对我的代码中的循环保留和内存泄漏有点 obsessed/paranoid ,在经历了一些带有各种内存问题的噩梦般的调试之后,所以将来我想把它们扼杀在芽。但是在阅读和学习了很多关于 Swift 中的 ARC 和保留循环之后,虽然它是有道理的,但我仍然没有足够的 "intuitive" 或自然的感觉,对我有信心在我编码时可以发现一个或缺少一个。所以我开始变得有点偏执,我什至在没有意识到的情况下用基本的东西创建了保留周期。
那么,考虑到这一点,为什么任何使用在其外部声明的变量的普通函数都不会创建保留循环?例如:
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 中的引用。那么,如果我不捕获 weak/unowned self,这会创建一个保留周期吗?在我看来,这么简单的事情会导致问题,这似乎很荒谬……或者是这样吗?在嵌套函数中怎么样,像这样:
func someFunction () -> String {
let a = "I'm letter a"
func addLetters () -> String {
let newString = a + "bcdefg"
return newString
}
let moreLetters = addLetters()
return moreLetters
}
这也会创建一个保留周期吗? (是的,我知道这是执行简单任务的一种复杂方式;我只是以这段代码为例来说明我的观点)。
我是不是变得超级偏执,对事情想得太多了?
首先,您需要了解基本的保留循环是如何形成的。当一个对象A同时强引用一个对象B时,就会形成一个retain cycle。对象 B 也强烈引用对象 A。
让我们看看您的第一段代码。
class someClass {
let a = "I'm letter a"
let moreLetters = addLetters()
func addLetters () -> String {
let newString = a + "bcdefg"
return newString
}
}
实际上,class 本身永远无法创建保留循环,所以让我们添加一些代码来创建一个对象:
var obj = someClass()
首先,a
被初始化为"I'm letter a"。之后,通过调用方法addLetters
来初始化moreLetters
。在方法returns之后,moreLetters
被初始化为"I'm letter abcdefg"。到目前为止一切顺利。
现在我们将 obj
设置为 nil
:
obj = nil
如果形成了一个保留循环,obj
将不会被取消初始化。然而,实际上,obj
被取消初始化,因为没有任何东西持有对 obj
!
的强引用
"Wait a minute!"你说,"But the method addLetters
still refers to someClass
because it has a
in it!"嗯,其实addLetters
已经return了!所以,里面的一切都已经不重要了!另外,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
}
没有形成保留循环,因为甚至没有引用类型!没有要创建的对象。您在第二个代码中所做的只是使用字符串,它们是值类型。即使有class,也不会形成retain cycle,因为正如我所说,当你将obj
设置为nil
时,其中的所有方法"disappear",因为方法属于对象。
那些我必须写 [weak self]
或保留循环形式的闭包呢?
那些闭包是转义闭包。正常的闭包在 return 之后被取消初始化。但是,转义闭包会被某些对象保留,因此不会立即取消初始化。有关详细信息,请参阅
抱歉,但我知道这是一个非常愚蠢的问题,我已经 'know' 找到了答案,但我需要有人向我清楚地解释为什么答案是这样。
最近,我对我的代码中的循环保留和内存泄漏有点 obsessed/paranoid ,在经历了一些带有各种内存问题的噩梦般的调试之后,所以将来我想把它们扼杀在芽。但是在阅读和学习了很多关于 Swift 中的 ARC 和保留循环之后,虽然它是有道理的,但我仍然没有足够的 "intuitive" 或自然的感觉,对我有信心在我编码时可以发现一个或缺少一个。所以我开始变得有点偏执,我什至在没有意识到的情况下用基本的东西创建了保留周期。
那么,考虑到这一点,为什么任何使用在其外部声明的变量的普通函数都不会创建保留循环?例如:
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 中的引用。那么,如果我不捕获 weak/unowned self,这会创建一个保留周期吗?在我看来,这么简单的事情会导致问题,这似乎很荒谬……或者是这样吗?在嵌套函数中怎么样,像这样:
func someFunction () -> String {
let a = "I'm letter a"
func addLetters () -> String {
let newString = a + "bcdefg"
return newString
}
let moreLetters = addLetters()
return moreLetters
}
这也会创建一个保留周期吗? (是的,我知道这是执行简单任务的一种复杂方式;我只是以这段代码为例来说明我的观点)。
我是不是变得超级偏执,对事情想得太多了?
首先,您需要了解基本的保留循环是如何形成的。当一个对象A同时强引用一个对象B时,就会形成一个retain cycle。对象 B 也强烈引用对象 A。
让我们看看您的第一段代码。
class someClass {
let a = "I'm letter a"
let moreLetters = addLetters()
func addLetters () -> String {
let newString = a + "bcdefg"
return newString
}
}
实际上,class 本身永远无法创建保留循环,所以让我们添加一些代码来创建一个对象:
var obj = someClass()
首先,a
被初始化为"I'm letter a"。之后,通过调用方法addLetters
来初始化moreLetters
。在方法returns之后,moreLetters
被初始化为"I'm letter abcdefg"。到目前为止一切顺利。
现在我们将 obj
设置为 nil
:
obj = nil
如果形成了一个保留循环,obj
将不会被取消初始化。然而,实际上,obj
被取消初始化,因为没有任何东西持有对 obj
!
"Wait a minute!"你说,"But the method addLetters
still refers to someClass
because it has a
in it!"嗯,其实addLetters
已经return了!所以,里面的一切都已经不重要了!另外,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
}
没有形成保留循环,因为甚至没有引用类型!没有要创建的对象。您在第二个代码中所做的只是使用字符串,它们是值类型。即使有class,也不会形成retain cycle,因为正如我所说,当你将obj
设置为nil
时,其中的所有方法"disappear",因为方法属于对象。
那些我必须写 [weak self]
或保留循环形式的闭包呢?
那些闭包是转义闭包。正常的闭包在 return 之后被取消初始化。但是,转义闭包会被某些对象保留,因此不会立即取消初始化。有关详细信息,请参阅