objective c - إشارة ضعيفة إلى NSTimer الهدف لمنع الاحتفاظ دورة




objective-c automatic-ref-counting (6)

إذا كنت تستخدم Swift هنا ، فسيتم مؤقت الإلغاء التلقائي:

https://gist.github.com/evgenyneu/516f7dcdb5f2f73d7923

الموقت يلغي نفسه تلقائيا على deinit .

var timer: AutoCancellingTimer? // Strong reference

func startTimer() {
  timer = AutoCancellingTimer(interval: 1, repeats: true) {
    print("Timer fired")
  }
}

أنا أستخدم 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];

لقد سمعت أنه يجب إبطال الموقت (أعتقد أن تحريره من حلقة التشغيل). ولكن يمكننا أن نفعل ذلك في dealloc لدينا ، أليس كذلك؟

- (void) dealloc {
    [timer invalidate];
}

هل هذا خيارا قابلا للتطبيق؟ لقد رأيت الكثير من الطرق التي يتعامل بها الأشخاص مع هذه المشكلة ، لكنني لم أرها.


إذا لم تكن مهتمًا بالدقة بالميلي ثانية لأحداث المؤقت ، فيمكنك استخدام dispatch_after & __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];
    });
}

لا يوجد NSTimerproperty ، لا توجد أشياء غير صالحة / غير صالحة للإستخدام ولا يوجد كائن وكيل ، فقط طريقة نظيفة بسيطة.

الجانب السلبي لهذا النهج هو أنه (على عكس NSTimer ) فإن وقت تنفيذ الكتلة (التي تحتوي على [weakSelf doSomethingRepeatedly]; ) سوف تؤثر على جدولة الأحداث.


سويفت 4 الإصدار. يجب استدعاء إبطال قبل dealloc.

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()
    }
}

استعمال

func  startTimer() {
    timerProxy = TimerProxy(withInterval: 10,
                            repeats: false,
                            timerHandler: { [weak self] in
                                self?.fireTimer()
    })
}

@objc func fireTimer() {
    timerProxy?.invalidate()
    timerProxy = nil
}

في 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 تم NSTimer على إشارة ضعيفة إلى self ، حتى تتمكن من استدعاء طريقة غير deinit في deinit .


مع النظرية والتطبيق. حل تومي لا يعمل.

نظريا ، __ مثيل ضعيف هو كمعلمة ، في تنفيذ

[NSTimer scheduledTimerWithTimeInterval:target:selector: userInfo: repeats:],

سيتم الاحتفاظ الهدف لا يزال.

يمكنك تطبيق وكيل ، والذي يحتفظ بالمرجع الضعيف ومكالمة التوجيه الأمامي إلى الذات ، ثم تمرير الوكيل كهدف. مثل YYWeakProxy.


سويفت 3

هدف التطبيق <iOS 10 :

تطبيق WeakTimer ( GitHubGist ) GitHubGist :

final class WeakTimer {

    fileprivate weak var timer: Timer?
    fileprivate weak var target: AnyObject?
    fileprivate let action: (Timer) -> Void

    fileprivate init(timeInterval: TimeInterval,
         target: AnyObject,
         repeats: Bool,
         action: @escaping (Timer) -> Void) {
        self.target = target
        self.action = action
        self.timer = Timer.scheduledTimer(timeInterval: timeInterval,
                                          target: self,
                                          selector: #selector(fire),
                                          userInfo: nil,
                                          repeats: repeats)
    }

    class func scheduledTimer(timeInterval: TimeInterval,
                              target: AnyObject,
                              repeats: Bool,
                              action: @escaping (Timer) -> Void) -> Timer {
        return WeakTimer(timeInterval: timeInterval,
                         target: target,
                         repeats: repeats,
                         action: action).timer!
    }

    @objc fileprivate func fire(timer: Timer) {
        if target != nil {
            action(timer)
        } else {
            timer.invalidate()
        }
    }
}

الاستعمال:

let timer = WeakTimer.scheduledTimer(timeInterval: 2,
                                     target: self,
                                     repeats: true) { [weak self] timer in
                                         // Place your action code here.
}

timer هو مثيل لفئة Timer القياسية ، بحيث يمكنك استخدام جميع الطرق المتاحة (مثل invalidate ، fire ، غير fireDate ، fireDate وغيرها).
سيتم إلغاء تخصيص مثيل timer عندما يتم إلغاء تخصيص self أو عند انتهاء وظيفة المؤقت (على سبيل المثال repeats == false ).

هدف التطبيق> = iOS 10 :
تطبيق الموقت القياسي:

open class func scheduledTimer(withTimeInterval interval: TimeInterval, 
                               repeats: Bool, 
                               block: @escaping (Timer) -> Swift.Void) -> Timer

الاستعمال:

let timer = Timer.scheduledTimer(withTimeInterval: 2, repeats: true) { [weak self] timer in
    // Place your action code here.
}




retain-cycle