如何停止班级计时器? - 斯威夫特

时间:2017-09-05 06:50:49

标签: ios swift memory-management timer closures

今天我正在阅读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")
        }

    }

}

弹出到第一个视图控制器后,此计时器不会结束,它将连续打印“计时器”。如何停止?,下面是调试内存图的屏幕截图。

enter image description here

如上图所示,我们可以看到没有第二个视图控制器的实例,但是计时器将连续执行!

如果我在计时器关闭时持有或使用强烈的自我参考,那么我相信它没关系。但是根据上面的代码,我没有持有或使用任何强大的参考,它也显示在调试内存图中(弹出后没有参考secondviewcontroller)。

如果我使用下面的代码,那么我接受计时器正在运行,因为下面的代码是强参考循环。

  

代码2:

class ViewController2: UIViewController {

override func viewDidLoad() {
    super.viewDidLoad()

        Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { (_) in
            print("\(self.view.tag)")
        }

    }

}

以下是它的调试内存图,弹出到第一个视图控制器后,可以看到第二个视图控制器ref仍然存在

enter image description here

根据我的Code 2部分,我可以接受okk它正在运行,因为viewcontroller2仍在内存中并且正在运行。但在我的Code 1部分中,没有viewcontrller2在内存中的迹象,它的计时器有效吗?为什么? 如何在不将计时器作为实例的情况下停止此操作。

2 个答案:

答案 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)

您只能通过使计时器成为类级变量并在invalidate或类似方法中调用viewDidDisappear来从内存中删除计时器。

计时器的工作方式是,当您安排计时器时,CFTimer会添加到CFRunLoop,并会创建一个名为NSTimerBlockTarget的内容。你可以从内存图中看到这个:

enter image description here

只要CFTimer处于活动状态,计时器就不会停止。当您调用invalidate时,它将从运行循环中删除计时器。