NSFileManager观察目录

9

如何使用NSFileManager监控目录?

我希望我的应用程序在运行时能够检测到文档目录中的文件何时被删除/添加。


请参考以下链接,了解如何在OSX中监控目录的变化而不使用FSEvent:http://stackoverflow.com/questions/7209811/monitoring-a-directory-for-changes-in-osx-without-fsevent - Marek Sebera
2
可能是在Objective-C中观察文件或文件夹的重复问题。 - Marek Sebera
2
这个问题被标记为iPhone。而且那些链接的答案在iOS上都不起作用。 - Matthias Bauch
2个回答

12

这是我自己用Swift编写的DirectoryWatcher版本,使用GCD而不是Mach,并使用闭包代替委托。

import Foundation

@objc public class DirectoryWatcher : NSObject {
    override public init() {
        super.init()
    }

    deinit {
        stop()
    }

    public typealias Callback = (_ directoryWatcher: DirectoryWatcher) -> Void

    @objc public convenience init(withPath path: String, callback: @escaping Callback) {
        self.init()
        if !watch(path: path, callback: callback) {
            assert(false)
        }
    }

    private var dirFD : Int32 = -1 {
        didSet {
            if oldValue != -1 {
                close(oldValue)
            }
        }
    }
    private var dispatchSource : DispatchSourceFileSystemObject?

    @objc public func watch(path: String, callback: @escaping Callback) -> Bool {
        // Open the directory
        dirFD = open(path, O_EVTONLY)
        if dirFD < 0 {
            return false
        }

        // Create and configure a DispatchSource to monitor it
        let dispatchSource = DispatchSource.makeFileSystemObjectSource(fileDescriptor: dirFD, eventMask: .write, queue: DispatchQueue.main)
        dispatchSource.setEventHandler {[unowned self] in
            callback(self)
        }
        dispatchSource.setCancelHandler {[unowned self] in
            self.dirFD = -1
        }
        self.dispatchSource = dispatchSource

        // Start monitoring
        dispatchSource.resume()

        // Success
        return true
    }

    @objc public func stop() {
        // Leave if not monitoring
        guard let dispatchSource = dispatchSource else {
            return
        }

        // Don't listen to more events
        dispatchSource.setEventHandler(handler: nil)

        // Cancel the source (this will also close the directory)
        dispatchSource.cancel()
        self.dispatchSource = nil
    }
}

使用它类似于苹果的DirectoryWatcher示例,像这样:

let directoryWatcher = DirectoryWatcher(withPath: "/path/to/the/folder/you/want/to/monitor/", callback: {
    print("the folder changed")
})

销毁该对象将停止监视,或者您可以明确地停止它。

directoryWatcher.stop()

它应该与Objective C兼容,就像它写的那样(未经测试)。使用它将是这样的:

DirectoryWatcher *directoryWatcher = [DirectoryWatcher.alloc initWithPath: @"/path/to/the/folder/you/want/to/monitor/" callback: ^(DirectoryWatcher *directoryWatcher) {
    NSLog(@"the folder changed")
}];

停止它类似

[directoryWatcher stop];

应认真考虑将此作为主要答案,因为原始答案已经过时。尽管原始文档引用是正确的,但原始代码已经非常陈旧,几乎已经消失(根据评论)。而且所提到的博客文章实际上利用了直接运行循环操作(在 Swift 时代有点陌生)。 - markgo2k
不错的解决方案,但无法跟踪子文件夹。 - KamyFC
@kamyFC 这是真的,但我不知道有哪些会这样做(苹果的DirectoryWatcher会吗?) - John Stephen
这个在Linux上能用吗? - DrMickeyLauer

6
请查看苹果文档中的《内核队列:一种替代文件系统事件的方式》。在 AVPlayerDemo 中有一个 iOS 示例(查看 DirectoryWatcher 类)。
此外,请查看 Directory Monitor 博客文章。

2
苹果公司已在DirectoryWatcher上添加了官方示例 https://developer.apple.com/library/ios/samplecode/DocInteraction/Introduction/Intro.html - Roman Blachman
苹果公司已经移动了示例,这是新链接 https://developer.apple.com/library/content/samplecode/AVPlayerDemo - John Stephen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接