作为 NSOperation 或线程的后台进程,用于监视和更新文件



我想检查pdf文件是否被更改,如果被更改,我想更新相应的视图。我不知道使用后台进程作为线程还是作为 NSOperation 来执行此任务更合适。苹果文档说:"非常适合NSOperation的任务示例包括网络请求,图像大小调整,文本处理或任何其他可重复的,结构化的,长时间运行的任务,这些任务会产生相关的状态或数据。但是,如果没有一点监督,简单地将计算包装到一个对象中并不能做很多事情。

此外,如果我从文档中正确理解,一旦启动的线程在执行期间无法停止,而 NSOperation 可以暂停或停止,并且它们还可以依靠依赖关系来等待另一个任务的完成。

此任务的工作流应或多或少为下图:

任务工作流

我设法在发送 .write 类型的通知后让处理程序工作。例如,如果我监视 *.txt 文件,则一切按预期工作,并且我只收到一个通知。但是我正在监视一个由pdflatex从终端生成的pdf文件,因此我收到了近15个通知。如果我更改为".attrib",我会收到 3 条通知。我只需要调用处理程序一次,而不是 15 或 3 次。您知道我该怎么做还是无法使用调度源吗?也许有一种方法可以只执行一次调度工作项?

我尝试像这样实现它(这是在FileMonitor类中):

func startMonitoring()
{
....
let fileSystemRepresentation = fileManager.fileSystemRepresentation(withPath: fileStringURL)
let fileDescriptor = open(fileSystemRepresentation, O_EVTONLY)
let newfileMonitorSource = DispatchSource.makeFileSystemObjectSource(fileDescriptor: fileDescriptor,
eventMask: .attrib,
queue: queue)
newfileMonitorSource.setEventHandler(handler: 
{
self.queue.async 
{  
print(" n received first write event, removing handler..." )
self.newfileMonitorSource.setEventHandler(handler: nil)
self.test()
}
})
self.fileMonitorSource = newfileMonitorSource
fileMonitorSource!.resume()
}

func test()
{
fileMonitorSource?.cancel()
print(" restart monitoring ")
startMonitoring()
}  

我尝试在test()中重新分配处理程序,但它不起作用(如果重新生成pdf文件,则不会执行新处理程序中的内容),对我来说,以这种方式执行此操作,这似乎有点样板代码。我还尝试了以下方法:

  • 暂停 startMonitoring() 的 setEventHandler 中的 DispatchSource(传递 nil),但是当我恢复它时,我得到了剩余的 .write 事件。

  • 取消 DispatchSource 对象
  • 并调用 startMonitoring(),如上面的代码所示,但通过这种方式,每次收到事件时我都会创建和销毁 DispatchSource 对象,我不喜欢,因为 cancel() 函数只有在用户决定禁用我正在实现的这个功能时才调用

我将尝试更好地编写应用程序的工作流程,以便您可以更清楚地了解我在做什么:

  • 当应用程序启动时,函数会设置窗口首选项的某些复选框的默认值。用户可以修改此复选框。因此,当用户打开pdf文件时,想法是在后台线程中启动以下任务:

    • 我创建了一个新的队列,称它为 A 并无限异步启动,同时我检查 UserDefault 复选框的值(我用它来重新加载和更新 pdf 文件),可能会发生两件事

      • 如果用户将该值设置为 off 并且已加载 PDF 文档,则可能有两种情况:

        • 如果当前没有文件监视(应用启动时):继续选中复选框值
        • 如果当前有监视文件:停止它
      • 如果用户将值设置为 on 并且 pdf 文档已加载到此后台线程(相同的队列 A)中,我将创建一个类监视器(可以是 NSThread 的子类,也可以是使用上述 DispatchSourceSystemObject 的类),然后我将调用 startMonitoring() 来检查日期或 .write 事件,当有更改时,它将调用处理程序。基本上,此处理程序应调用主线程(主队列)并检查文件是否可以加载或已损坏,如果是,请更新视图。

注意:无限 while 循环(应该在后台运行),检查与我正在实现的功能相关的 UserDefault,当用户打开 pdf 文件时启动。

由于上述问题(多个处理程序调用),当用户将复选框设置为关闭时,我应该使用 cancel() 函数,而不是每次收到 .write 事件时创建/销毁 DispatchSource 对象。