今天我正在阅读swift编程指南here中的“强大的闭包周期”,而我的问题就是基于此。
我在导航控制器中嵌入了两个视图控制器,我在第一个视图控制器中没有任何东西只是用于segue到secondviewcontroller而我在secondViewcontroller又名ViewController2中有下面的代码。
代码1:
class ViewController2: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { (_) in
print("timer")
}
}
}
弹出到第一个视图控制器后,此计时器不会结束,它将连续打印“计时器”。如何停止?,下面是调试内存图的屏幕截图。
如上图所示,我们可以看到没有第二个视图控制器的实例,但是计时器将连续执行!
如果我在计时器关闭时持有或使用强烈的自我参考,那么我相信它没关系。但是根据上面的代码,我没有持有或使用任何强大的参考,它也显示在调试内存图中(弹出后没有参考secondviewcontroller)。
如果我使用下面的代码,那么我接受计时器正在运行,因为下面的代码是强参考循环。
代码2:
class ViewController2: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { (_) in
print("\(self.view.tag)")
}
}
}
以下是它的调试内存图,弹出到第一个视图控制器后,可以看到第二个视图控制器ref仍然存在。
根据我的Code 2部分,我可以接受okk它正在运行,因为viewcontroller2仍在内存中并且正在运行。但在我的Code 1部分中,没有viewcontrller2在内存中的迹象,它的计时器有效吗?为什么? 如何在不将计时器作为实例的情况下停止此操作。
答案 0 :(得分:1)
您可以在SecondVC弹出之前停止计时器
您需要实例变量
var timer : Timer;
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if timer != nil && self.isMovingFromParentViewController {
timer.invalidate()
timer = nil
}
}
修改强>
好的,我接受,但为什么xcode没有在内存图中显示它。如 没有viewcontroller2的实例。
所以基本上你的问题是你想知道为什么你的第一个代码中没有ViewController2引用,以及当你在内存图中使用第二个代码时为什么会有一个ViewController2引用:)
什么是街区?
为了理解它,你必须理解块/闭包的概念。 块/闭包只不过是一个引用计数对象,就像堆内存中的结构一样。
当您将块作为参数传递时,您将堆中的此对象从一个函数传递到另一个函数。
这些是引用计数对象,因此ARC的所有规则也适用于它们。
上下文捕获
闭包与其他当代部分(实例和静态方法)不同的唯一原因是ContextCapturing。块能够捕获在其上下文中声明的变量,而实例和静态方法不能。
什么是上下文捕获?
每次编译器看到closure / block的语法时,它都会将Closure中的所有代码复制到堆中的对象。除此之外,它还复制了在其中访问的所有变量。这就是闭包能够访问在其范围外声明的变量的原因。
你为什么告诉我这一切?
在您的代码中
Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { (_) in
print("\(self.view.tag)")
}
当你在关闭内部访问self时,你的第二个VC被复制到堆内存中的闭包对象。因为它的第二个VC的强引用引用计数增加了+1。
因此,当您点击后退按钮时,第二个VC未被释放,因为ARC仅在其引用计数为0时才删除对象。
在代码中
Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { (_) in
print("timer")
}
当你的SecondVC参考计数从未改变其原始值时,你还没有自我访问。因此当你点击后退按钮deist会在第二个VC上调用
这就是为什么你不会在第一个代码中看到第二个VC的引用,正如你在第二个代码中看到的那样。
为什么你看到Timer的重新定义已经由Sweeper在他的回答中解释了:)再次解释没有意义
答案 1 :(得分:1)