今天,我正在阅读快速编程指南here的“封闭的强大参考周期”,而我的问题就是基于此。

我在导航控制器中嵌入了两个视图控制器,在第一个视图控制器中什么也没有,仅用于连接到secondviewcontroller,并且在secondViewcontroller中也有以下代码,也称为ViewController2。

代码1:

class ViewController2: UIViewController {

override func viewDidLoad() {
    super.viewDidLoad()

        Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { (_) in
            print("timer")
        }

    }

}

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

ios - 如何停止类(class)计时器? - swift-LMLPHP

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

如果我在定时器关闭时持有或使用自己的强烈推荐,那么我相信这是可以的。但是按照上面的代码,我没有持有或使用任何强引用,并且它也显示在Debug内存图中(弹出后没有secondviewcontroller的引用)。

如果我使用下面的代码,那么我接受计时器正在运行,因为按照下面的代码,它具有较强的引用周期。

代码2:
class ViewController2: UIViewController {

override func viewDidLoad() {
    super.viewDidLoad()

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

    }

}

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

ios - 如何停止类(class)计时器? - swift-LMLPHP

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

最佳答案

您可以在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时,您的Second VC将被复制到堆内存中的闭包对象。因为第二VC的强大参考引用计数增加了+1。

因此,当您单击后退按钮时,不会释放第二个VC,因为ARC仅在其引用计数为0时才删除对象。

在您的代码中
    Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { (_) in
        print("timer")
    }

U尚未访问self,因此当SecondVC引用计数从未更改为其原始值时。因此,当您点击后退按钮时,将在第二个VC上调用

这就是为什么您在第一代码中看不到第二个VC的引用的原因。

为什么您会看到Sweeper在他的回答中已经解释了Timer的延迟:)没有必要再次解释

关于ios - 如何停止类(class)计时器? - swift ,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/46048628/

10-15 15:23