Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/objective-c/25.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Objective c 对NSTimer目标的弱引用可防止保留周期_Objective C_Automatic Ref Counting_Nstimer_Retain Cycle - Fatal编程技术网

Objective c 对NSTimer目标的弱引用可防止保留周期

Objective c 对NSTimer目标的弱引用可防止保留周期,objective-c,automatic-ref-counting,nstimer,retain-cycle,Objective C,Automatic Ref Counting,Nstimer,Retain Cycle,我使用的是NSTimer如下: timer = [NSTimer scheduledTimerWithTimeInterval:30.0f target:self selector:@selector(tick) userInfo:nil repeats:YES]; 当然,NSTimer会保留创建保留周期的目标。此外,self不是UIViewController,因此我没有类似于viewDidUnload的东西,可以使计时器失效以中断循环。所以我想知道我是否可以使用弱引用: __weak id

我使用的是
NSTimer
如下:

timer = [NSTimer scheduledTimerWithTimeInterval:30.0f target:self selector:@selector(tick) userInfo:nil repeats:YES];
当然,
NSTimer
会保留创建保留周期的目标。此外,
self
不是UIViewController,因此我没有类似于
viewDidUnload
的东西,可以使计时器失效以中断循环。所以我想知道我是否可以使用弱引用:

__weak id weakSelf = self;
timer = [NSTimer scheduledTimerWithTimeInterval:30.0f target:weakSelf selector:@selector(tick) userInfo:nil repeats:YES];
我听说计时器必须失效(我想应该从运行循环中释放它)。但我们可以在我们的交易中这样做,对吗

- (void) dealloc {
    [timer invalidate];
}
这是一个可行的选择吗?我见过很多人处理这个问题的方法,但我没有见过。

不管weakSelf是否弱,计时器仍然保留对象,因此仍然有一个保留周期。由于运行循环保留计时器,因此可以(我建议)保留指向计时器的弱指针:

NSTimer* __weak timer = [NSTimer scheduledTimerWithTimeInterval:30.0f target: self selector:@selector(tick) userInfo:nil repeats:YES];
“关于使无效”您的做法是正确的。

建议的代码:

__weak id weakSelf = self;
timer = [NSTimer scheduledTimerWithTimeInterval:30.0f target:weakSelf selector:@selector(tick) userInfo:nil repeats:YES];
具有以下效果:(i)对自我的引用较弱;(ii)读取该弱引用是为了提供指向
NSTimer
的指针。它不会产生使用弱引用创建
NSTimer
的效果。该代码与使用
\u strong
引用之间的唯一区别在于,如果在给定的两行之间取消分配self,则将
nil
传递给计时器

您可以做的最好的事情是创建一个代理对象。比如:

[...]
@implementation BTWeakTimerTarget
{
    __weak target;
    SEL selector;
}

[...]

- (void)timerDidFire:(NSTimer *)timer
{
    if(target)
    {
        [target performSelector:selector withObject:timer];
    }
    else
    {
        [timer invalidate];
    }
}
@end
BTWeakTimerTarget *target = [[BTWeakTimerTarget alloc] initWithTarget:self selector:@selector(tick)];
timer = [NSTimer scheduledTimerWithTimeInterval:30.0 target:target selector:@selector(timerDidFire:) ...];
然后你会这样做:

[...]
@implementation BTWeakTimerTarget
{
    __weak target;
    SEL selector;
}

[...]

- (void)timerDidFire:(NSTimer *)timer
{
    if(target)
    {
        [target performSelector:selector withObject:timer];
    }
    else
    {
        [timer invalidate];
    }
}
@end
BTWeakTimerTarget *target = [[BTWeakTimerTarget alloc] initWithTarget:self selector:@selector(tick)];
timer = [NSTimer scheduledTimerWithTimeInterval:30.0 target:target selector:@selector(timerDidFire:) ...];
或者甚至向形式为
+scheduledTimerWithTimeInterval:target:selector:…
的BTWeakTimerTarget添加一个类方法,以创建该代码的更整洁的形式。您可能希望公开真实的
NSTimer
,以便
使其无效,否则将建立以下规则:

  • 定时器没有保留真正的目标
  • 在实际目标开始(可能已经完成)释放后,计时器将触发一次,但该触发将被忽略,计时器将失效

  • 如果您不太关心计时器事件的毫秒精度,那么可以使用dispatch\u after&\uu-weak而不是NSTimer来执行此操作。以下是代码模式:

    - (void) doSomethingRepeatedly
    {
        // Do it once
        NSLog(@"doing something …");
    
        // Repeat it in 2.0 seconds
        __weak typeof(self) weakSelf = self;
        double delayInSeconds = 2.0;
        dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
        dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
            [weakSelf doSomethingRepeatedly];
        });
    }
    
    没有NSTimer@property,没有invalidate/runloop内容,也没有代理对象,只是一个简单的clean方法


    这种方法的缺点是(与
    NSTimer
    )块的执行时间(包含
    [weakSelf dosomethingreepeatedly];
    )将影响事件的调度。

    在Swift中,我定义了一个
    WeakTimer
    助手类:

    /// A factory for NSTimer instances that invoke closures, thereby allowing a weak reference to its context.
    struct WeakTimerFactory {
      class WeakTimer: NSObject {
        private var timer: NSTimer!
        private let callback: () -> Void
    
        private init(timeInterval: NSTimeInterval, userInfo: AnyObject?, repeats: Bool, callback: () -> Void) {
          self.callback = callback
          super.init()
          self.timer = NSTimer(timeInterval: timeInterval, target: self, selector: "invokeCallback", userInfo: userInfo, repeats: repeats)
        }
    
        func invokeCallback() {
          callback()
        }
      }
    
      /// Returns a new timer that has not yet executed, and is not scheduled for execution.
      static func timerWithTimeInterval(timeInterval: NSTimeInterval, userInfo: AnyObject?, repeats: Bool, callback: () -> Void) -> NSTimer {
        return WeakTimer(timeInterval: timeInterval, userInfo: userInfo, repeats: repeats, callback: callback).timer
      }
    }
    
    然后你可以像这样使用它:

    let timer = WeakTimerFactory.timerWithTimeInterval(interval, userInfo: userInfo, repeats: repeats) { [weak self] in
      // Your code here...
    }
    

    返回的
    NSTimer
    self
    的引用较弱,因此如果使用Swift,可以在
    deinit

    中调用其
    invalidate
    方法这里有一个自动取消计时器:

    var timer: AutoCancellingTimer? // Strong reference
    
    func startTimer() {
      timer = AutoCancellingTimer(interval: 1, repeats: true) {
        print("Timer fired")
      }
    }
    

    定时器会在
    deinit
    上自动取消自身

    var timer: AutoCancellingTimer? // Strong reference
    
    func startTimer() {
      timer = AutoCancellingTimer(interval: 1, repeats: true) {
        print("Timer fired")
      }
    }
    

    iOS 10macOS 10.12“Sierra”引入了一种新方法,因此您可以捕获
    self
    如下:

    __weak MyClass* weakSelf = self;
    _timer = [NSTimer scheduledTimerWithTimeInterval:1.0 repeats:YES block:^(NSTimer* t) {
        MyClass* _Nullable strongSelf = weakSelf;
        [strongSelf doSomething];
    }];
    
    Swift 3中的等效性:

    _timer = Timer(timeInterval: 1.0, repeats: true) { [weak self] _ in
        self?.doSomething()
    }
    

    如果您仍然需要以iOS 9或更低版本为目标(此时您应该这样做),则无法使用此方法,因此您仍然需要在其他答案中使用代码。

    Swift 3

    应用程序目标

    自定义WeakTimer()实现:

    用法:

    timer
    是标准类
    timer
    的实例,因此您可以使用所有可用的方法(例如
    invalidate
    fire
    isValid
    fireDate
    等)。
    timer
    实例将在
    self
    解除分配或计时器的作业完成时解除分配(例如,
    repeats==false

    应用程序目标>=iOS 10
    标准计时器实现:

    用法:


    Swift 4版本。必须在解除锁定之前调用Invalidate

    class TimerProxy {
    
        var timer: Timer!
        var timerHandler: (() -> Void)?
    
        init(withInterval interval: TimeInterval, repeats: Bool, timerHandler: (() -> Void)?) {
            self.timerHandler = timerHandler
            timer = Timer.scheduledTimer(timeInterval: interval,
                                         target: self,
                                         selector: #selector(timerDidFire(_:)),
                                         userInfo: nil,
                                         repeats: repeats)
        }
    
        @objc func timerDidFire(_ timer: Timer) {
            timerHandler?()
        }
    
        func invalidate() {
            timer.invalidate()
        }
    }
    
    用法


    理论和实践的结合。汤米的解决方案是行不通的

    理论上,弱实例作为参数,在实现

    [NSTimer scheduledTimerWithTimeInterval:target:selector: userInfo: repeats:],
    
    目标仍将保留


    您可以实现一个代理,它保存弱引用并将选择器调用转发给self,然后将代理作为目标传递。例如YYWeakProxy

    但是runloop不会保留对计时器的强引用吗?计时器会保留对
    self
    的强引用,并且
    dealloc
    不会发生?至少它避免了保留循环。我仍然同意使用弱定时器更好。@Tommy是正确的,它不能避免保留周期。只要计时器没有失效,对象就永远不会死,如果只有dealloc使计时器失效,它也永远不会失效。弱计时器是毫无意义的,几乎从来都不是你想要的。计时器也可以调用弱选择器。@frangulyan不,它没有。它说“因为运行循环维护计时器”,这意味着运行循环使计时器保持活动状态。不管你是否让它活着,只要它被安排好,它就不会死,而取消计时器计划的官方方法是使它失效。它还表示“计时器保持对其目标的强引用”。因此,只要计时器处于活动状态,它也将保持其目标处于活动状态。所以,让计时器变弱不会修复任何东西,除非你在某个时候使它失效。太棒了,谢谢。最后,我制作了一个
    NSWeakTimer
    shell,它处理了bendytree的选择器接线。我查看了您的代码。目标不应是
    分配
    属性。相反,它应该是@Tommy描述的
    属性。取消分配后,分配属性不会变为
    nil
    ,而弱属性会变为。因此,您的
    if(target)
    检查将永远不会成为真的。@Pwner我检查了它并将赋值更改为弱,但它仍然不会使它无效。我该怎么办?@sftsz您是在使用runloop运行吗
    func  startTimer() {
        timerProxy = TimerProxy(withInterval: 10,
                                repeats: false,
                                timerHandler: { [weak self] in
                                    self?.fireTimer()
        })
    }
    
    @objc func fireTimer() {
        timerProxy?.invalidate()
        timerProxy = nil
    }
    
    [NSTimer scheduledTimerWithTimeInterval:target:selector: userInfo: repeats:],