Remove `ParasiticTask`, now `host` is a built-in parameter for all tasks.

This commit is contained in:
QuentinJin 2018-09-23 12:24:00 +08:00
parent 95ab16f3f7
commit 4242f84f07
6 changed files with 60 additions and 131 deletions

View File

@ -32,7 +32,6 @@
OBJ_48 /* Interval.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_12 /* Interval.swift */; }; OBJ_48 /* Interval.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_12 /* Interval.swift */; };
OBJ_49 /* Lock.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_13 /* Lock.swift */; }; OBJ_49 /* Lock.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_13 /* Lock.swift */; };
OBJ_50 /* Monthday.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_14 /* Monthday.swift */; }; OBJ_50 /* Monthday.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_14 /* Monthday.swift */; };
OBJ_51 /* ParasiticTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_15 /* ParasiticTask.swift */; };
OBJ_52 /* Period.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_16 /* Period.swift */; }; OBJ_52 /* Period.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_16 /* Period.swift */; };
OBJ_53 /* Schedule.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_17 /* Schedule.swift */; }; OBJ_53 /* Schedule.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_17 /* Schedule.swift */; };
OBJ_54 /* Task.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_18 /* Task.swift */; }; OBJ_54 /* Task.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_18 /* Task.swift */; };
@ -82,7 +81,6 @@
OBJ_12 /* Interval.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Interval.swift; sourceTree = "<group>"; }; OBJ_12 /* Interval.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Interval.swift; sourceTree = "<group>"; };
OBJ_13 /* Lock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Lock.swift; sourceTree = "<group>"; }; OBJ_13 /* Lock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Lock.swift; sourceTree = "<group>"; };
OBJ_14 /* Monthday.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Monthday.swift; sourceTree = "<group>"; }; OBJ_14 /* Monthday.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Monthday.swift; sourceTree = "<group>"; };
OBJ_15 /* ParasiticTask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParasiticTask.swift; sourceTree = "<group>"; };
OBJ_16 /* Period.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Period.swift; sourceTree = "<group>"; }; OBJ_16 /* Period.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Period.swift; sourceTree = "<group>"; };
OBJ_17 /* Schedule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Schedule.swift; sourceTree = "<group>"; }; OBJ_17 /* Schedule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Schedule.swift; sourceTree = "<group>"; };
OBJ_18 /* Task.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Task.swift; sourceTree = "<group>"; }; OBJ_18 /* Task.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Task.swift; sourceTree = "<group>"; };
@ -222,7 +220,6 @@
OBJ_8 /* Schedule */ = { OBJ_8 /* Schedule */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
OBJ_15 /* ParasiticTask.swift */,
66D9F43221563D7700D9F441 /* RunLoopTask.swift */, 66D9F43221563D7700D9F441 /* RunLoopTask.swift */,
OBJ_17 /* Schedule.swift */, OBJ_17 /* Schedule.swift */,
OBJ_18 /* Task.swift */, OBJ_18 /* Task.swift */,
@ -352,7 +349,6 @@
OBJ_48 /* Interval.swift in Sources */, OBJ_48 /* Interval.swift in Sources */,
OBJ_49 /* Lock.swift in Sources */, OBJ_49 /* Lock.swift in Sources */,
OBJ_50 /* Monthday.swift in Sources */, OBJ_50 /* Monthday.swift in Sources */,
OBJ_51 /* ParasiticTask.swift in Sources */,
OBJ_52 /* Period.swift in Sources */, OBJ_52 /* Period.swift in Sources */,
OBJ_53 /* Schedule.swift in Sources */, OBJ_53 /* Schedule.swift in Sources */,
OBJ_54 /* Task.swift in Sources */, OBJ_54 /* Task.swift in Sources */,

View File

@ -9,24 +9,34 @@ import Foundation
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
class DeinitObserver<T: AnyObject> { private var deinitObserverKey: Void = ()
private weak var observed: T? class DeinitObserver {
private var block: () -> Void private(set) weak var observable: AnyObject?
private var block: (() -> Void)?
private init(_ block: @escaping () -> Void) { private init(_ block: @escaping () -> Void) {
self.block = block self.block = block
} }
static func observe(_ observed: T, whenDeinit: @escaping () -> Void) { @discardableResult
let observer = DeinitObserver(whenDeinit) static func observe(_ observable: AnyObject, whenDeinit block: @escaping () -> Void) -> DeinitObserver {
var key: Void = () let observer = DeinitObserver(block)
objc_setAssociatedObject(observed, &key, observer, .OBJC_ASSOCIATION_RETAIN) objc_setAssociatedObject(observable, &deinitObserverKey, observer, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
return observer
}
func clear() {
block = nil
if let o = observable {
objc_setAssociatedObject(o, &deinitObserverKey, nil, .OBJC_ASSOCIATION_ASSIGN)
}
} }
deinit { deinit {
block() block?()
} }
} }

View File

@ -1,81 +0,0 @@
//
// ParasiticTask.swift
// Schedule
//
// Created by Quentin Jin on 2018/7/17.
//
import Foundation
extension Schedule {
/// Schedules a task with this schedule.
///
/// This method will receive a `host` object as a parameter,
/// the returned task will not retain this object, instead,
/// it will observe this object, when this object is dealloced,
/// the task will not be scheduled any more, something like parasitism.
///
/// This feature is very useful when you want a scheduled task live and die
/// with a controller.
///
/// - Parameters:
/// - queue: The queue to which the task will be dispatched.
/// - tag: The tag to be associated to the task.
/// - host: The object to be hosted on.
/// - onElapse: The action to do when time is out.
/// - Returns: The task just created.
@discardableResult
public func `do`(queue: DispatchQueue,
tag: String? = nil,
host: AnyObject,
onElapse: @escaping (Task) -> Void) -> Task {
return ParasiticTask(schedule: self, queue: queue, tag: tag, host: host, onElapse: onElapse)
}
/// Schedules a task with this schedule.
///
/// This method will receive a `host` object as a parameter,
/// the returned task will not retain this object, instead,
/// it will observe this object, when this object is dealloced,
/// the task will not scheduled any more, something like parasitism.
///
/// This feature is very useful when you want a scheduled task live and die
/// with a controller.
///
/// - Parameters:
/// - queue: The queue to which the task will be dispatched.
/// - tag: The tag to be associated to the task.
/// - host: The object to be hosted on.
/// - onElapse: The action to do when time is out.
/// - Returns: The task just created.
@discardableResult
public func `do`(queue: DispatchQueue,
tag: String? = nil,
host: AnyObject,
onElapse: @escaping () -> Void) -> Task {
return self.do(queue: queue, tag: tag, host: host, onElapse: { (_) in onElapse() })
}
}
private final class ParasiticTask: Task {
weak var parasitifer: AnyObject?
init(schedule: Schedule, queue: DispatchQueue?, tag: String?, host: AnyObject, onElapse: @escaping (Task) -> Void) {
super.init(schedule: schedule, queue: queue, tag: tag) { (task) in
guard (task as? ParasiticTask)?.parasitifer != nil else {
task.cancel()
return
}
onElapse(task)
}
self.parasitifer = host
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
DeinitObserver.observe(host) { [weak self] in
self?.cancel()
}
#endif
}
}

View File

@ -11,48 +11,30 @@ extension Schedule {
/// Schedules a task with this schedule. /// Schedules a task with this schedule.
/// ///
/// This method will receive a `host` object as a parameter,
/// the returned task will not retain this object, instead,
/// it will observe this object, when this object is dealloced,
/// the task will not be scheduled any more, something like parasitism.
///
/// This feature is very useful when you want a scheduled task live and die
/// with a controller.
///
/// - Parameters: /// - Parameters:
/// - queue: The queue to which the task will be dispatched. /// - mode: The mode in which to add the schedule.
/// - tag: The tag to be associated to the task.
/// - host: The object to be hosted on. /// - host: The object to be hosted on.
/// - onElapse: The action to do when time is out. /// - onElapse: The action to do when time is out.
/// - Returns: The task just created. /// - Returns: The task just created.
@discardableResult @discardableResult
public func `do`(mode: RunLoop.Mode = .default, public func `do`(mode: RunLoop.Mode = .default,
tag: String? = nil, host: AnyObject? = nil,
onElapse: @escaping (Task) -> Void) -> Task { onElapse: @escaping (Task) -> Void) -> Task {
return RunLoopTask(schedule: self, mode: mode, tag: tag, onElapse: onElapse) return RunLoopTask(schedule: self, mode: mode, host: host, onElapse: onElapse)
} }
/// Schedules a task with this schedule. /// Schedules a task with this schedule.
/// ///
/// This method will receive a `host` object as a parameter,
/// the returned task will not retain this object, instead,
/// it will observe this object, when this object is dealloced,
/// the task will not scheduled any more, something like parasitism.
///
/// This feature is very useful when you want a scheduled task live and die
/// with a controller.
///
/// - Parameters: /// - Parameters:
/// - queue: The queue to which the task will be dispatched. /// - mode: The mode in which to add the schedule.
/// - tag: The tag to be associated to the task.
/// - host: The object to be hosted on. /// - host: The object to be hosted on.
/// - onElapse: The action to do when time is out. /// - onElapse: The action to do when time is out.
/// - Returns: The task just created. /// - Returns: The task just created.
@discardableResult @discardableResult
public func `do`(mode: RunLoop.Mode = .default, public func `do`(mode: RunLoop.Mode = .default,
tag: String? = nil, host: AnyObject? = nil,
onElapse: @escaping () -> Void) -> Task { onElapse: @escaping () -> Void) -> Task {
return self.do(mode: mode, tag: tag) { (_) in return self.do(mode: mode, host: host) { (_) in
onElapse() onElapse()
} }
} }
@ -62,7 +44,7 @@ private final class RunLoopTask: Task {
var timer: Timer! var timer: Timer!
init(schedule: Schedule, mode: RunLoop.Mode, tag: String?, onElapse: @escaping (Task) -> Void) { init(schedule: Schedule, mode: RunLoop.Mode, host: AnyObject?, onElapse: @escaping (Task) -> Void) {
var this: Task? var this: Task?
@ -74,7 +56,7 @@ private final class RunLoopTask: Task {
RunLoop.current.add(timer, forMode: mode) RunLoop.current.add(timer, forMode: mode)
super.init(schedule: schedule, queue: nil, tag: tag) { (task) in super.init(schedule: schedule, queue: nil, host: host) { (task) in
guard let task = task as? RunLoopTask else { return } guard let task = task as? RunLoopTask else { return }
task.timer.fireDate = Date() task.timer.fireDate = Date()
} }

View File

@ -26,28 +26,30 @@ public struct Schedule {
/// ///
/// - Parameters: /// - Parameters:
/// - queue: The queue to which the task will be dispatched. /// - queue: The queue to which the task will be dispatched.
/// - tag: The tag to be associated to the task. /// - host: The object to be hosted on. When this object is dealloced,
/// the task will not scheduled any more.
/// - onElapse: The action to do when time is out. /// - onElapse: The action to do when time is out.
/// - Returns: The task just created. /// - Returns: The task just created.
@discardableResult @discardableResult
public func `do`(queue: DispatchQueue, public func `do`(queue: DispatchQueue,
tag: String? = nil, host: AnyObject? = nil,
onElapse: @escaping (Task) -> Void) -> Task { onElapse: @escaping (Task) -> Void) -> Task {
return Task(schedule: self, queue: queue, tag: tag, onElapse: onElapse) return Task(schedule: self, queue: queue, host: host, onElapse: onElapse)
} }
/// Schedules a task with this schedule. /// Schedules a task with this schedule.
/// ///
/// - Parameters: /// - Parameters:
/// - queue: The queue to which the task will be dispatched. /// - queue: The queue to which the task will be dispatched.
/// - tag: The tag to be associated to the task. /// - host: The object to be hosted on. When this object is dealloced,
/// the task will not scheduled any more.
/// - onElapse: The action to do when time is out. /// - onElapse: The action to do when time is out.
/// - Returns: The task just created. /// - Returns: The task just created.
@discardableResult @discardableResult
public func `do`(queue: DispatchQueue, public func `do`(queue: DispatchQueue,
tag: String? = nil, host: AnyObject? = nil,
onElapse: @escaping () -> Void) -> Task { onElapse: @escaping () -> Void) -> Task {
return self.do(queue: queue, tag: tag, onElapse: { (_) in onElapse() }) return self.do(queue: queue, host: host, onElapse: { (_) in onElapse() })
} }
} }

View File

@ -34,6 +34,10 @@ public class Task {
private lazy var _tags: Set<String> = [] private lazy var _tags: Set<String> = []
private lazy var _countOfExecutions: Int = 0 private lazy var _countOfExecutions: Int = 0
#if os(Linux)
private weak var _host: AnyObject? = TaskHub.shared
#endif
private lazy var _lifetime: Interval = Int.max.second private lazy var _lifetime: Interval = Int.max.second
private lazy var _lifetimeTimer: DispatchSourceTimer = { private lazy var _lifetimeTimer: DispatchSourceTimer = {
let timer = DispatchSource.makeTimerSource() let timer = DispatchSource.makeTimerSource()
@ -47,8 +51,8 @@ public class Task {
}() }()
init(schedule: Schedule, init(schedule: Schedule,
queue: DispatchQueue? = nil, queue: DispatchQueue?,
tag: String? = nil, host: AnyObject?,
onElapse: @escaping (Task) -> Void) { onElapse: @escaping (Task) -> Void) {
_iterator = schedule.makeIterator() _iterator = schedule.makeIterator()
@ -57,6 +61,14 @@ public class Task {
_actions.append(onElapse) _actions.append(onElapse)
_timer.setEventHandler { [weak self] in _timer.setEventHandler { [weak self] in
#if os(Linux)
guard self?._host != nil else {
self?.cancel()
return
}
#endif
self?.elapse() self?.elapse()
} }
@ -65,7 +77,18 @@ public class Task {
_timer.schedule(after: interval) _timer.schedule(after: interval)
_timeline.estimatedNextExecution = Date().adding(interval) _timeline.estimatedNextExecution = Date().adding(interval)
TaskHub.shared.add(self, withTag: tag) #if os(Linux)
_host = host
#else
if let host = host {
DeinitObserver.observe(host) { [weak self] in
self?.cancel()
}
}
#endif
TaskHub.shared.add(self)
_timer.resume() _timer.resume()
} }
@ -341,9 +364,6 @@ public class Task {
public func removeTag(_ tag: String) { public func removeTag(_ tag: String) {
removeTags(tag) removeTags(tag)
} }
}
extension Task {
/// Suspends all tasks that have the tag. /// Suspends all tasks that have the tag.
public static func suspend(byTag tag: String) { public static func suspend(byTag tag: String) {