LoginSignup
17
18

More than 5 years have passed since last update.

Cocoaでディレクトリ監視

Last updated at Posted at 2013-04-30

ディレクトリ監視、つまりファイルのリネーム、移動、削除、作成等々の、
監視を行いたいケースはあると思います。

しかしどうにも使いやすいNSなんちゃらのクラスは見当たりません。
しかし、CベースのAPIならばあるようです。
CoreServicesフレームワークを今回は利用します。

まずはCoreServices.frameworkをライブラリに追加します。
そしてヘッダーはこちらです。

#import <CoreServices/CoreServices.h>

で、メインプログラムなのですが、
1、FSEventStreamCreate
2、FSEventStreamScheduleWithRunLoop
3、FSEventStreamStart
の3ステップで可能です。

こちらのAPIを使うのですが、このままだとObjective-cに組み込みにくいため、
ラッパーを作成します。

以下はデリゲートパターンでの実装です。
こういうのを実装する時にはblocksでもいいのですが、
長いスパンで動作する通知系においては、
なんだかんだで循環参照やら気を配らなくていいデリゲートパターンが一番良い気がします。
iOSでは試していません。
ぶっちゃけiOSでディレクトリ監視なんてほとんど使わないとおもいますが・・・。

最低限の機能しかないので必要に応じてカスタマイズすることを想定しています。

ヘッダー(DirectoryObserver.h)

#import <Foundation/Foundation.h>

@protocol DirectoryObserverDelegate <NSObject>
- (void)onDirectoryChanged:(id)sender directories:(NSArray *)directories;
@end

@interface DirectoryObserver : NSObject
- (id)initWithDirectoryPath:(NSString *)path;

- (void)startObserving;
- (void)stopObserving;

@property (weak, nonatomic) id<DirectoryObserverDelegate> delegate;
@end

実装(DirectoryObserver.m)

#import "DirectoryObserver.h"

#import <CoreServices/CoreServices.h>

@interface DirectoryObserver()
- (void)onDirectoryChanged:(NSArray *)directories;
@end

void fs_event_callback(ConstFSEventStreamRef streamRef,
                       void *clientCallBackInfo,
                       size_t numEvents,
                       void *eventPaths,
                       const FSEventStreamEventFlags eventFlags[],
                       const FSEventStreamEventId eventIds[])
{
    char **paths = eventPaths;
    NSMutableArray *directories = [NSMutableArray array];
    for (int i = 0 ; i < numEvents ; i++) {
        char *path = paths[i];
        [directories addObject:[NSString stringWithUTF8String:path]];
    }

    DirectoryObserver *observer = (__bridge DirectoryObserver *)clientCallBackInfo;
    [observer onDirectoryChanged:directories];
}

@implementation DirectoryObserver
{
    NSString *_path;
    FSEventStreamRef _stream;
}
- (id)initWithDirectoryPath:(NSString *)path
{
    if(self = [super init])
    {
        _path = path;
    }
    return self;
}
- (void)startObserving
{
    if(_stream)
        return;

    FSEventStreamContext context = {0};
    context.info = (__bridge void *)self;
    CFTimeInterval latency = 0.1; /*監視間隔*/
    _stream = FSEventStreamCreate(kCFAllocatorDefault,
                                  fs_event_callback, /*コールバック*/
                                  &context, /*ユーザーデータのため*/
                                  (__bridge CFArrayRef)@[_path], /*監視するパス*/
                                  kFSEventStreamEventIdSinceNow, /*よくわかりません*/
                                  latency,
                                  kFSEventStreamCreateFlagNone); /*ひとまずNoneで*/

    FSEventStreamScheduleWithRunLoop(_stream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
    FSEventStreamStart(_stream);
}
- (void)stopObserving
{
    if(_stream == NULL)
        return;

    FSEventStreamStop(_stream);
    FSEventStreamUnscheduleFromRunLoop(_stream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
    FSEventStreamRelease(_stream);
    _stream = NULL;
}
- (void)onDirectoryChanged:(NSArray *)directories
{
    if(self.delegate)
       [self.delegate onDirectoryChanged:self directories:directories];
}
@end
17
18
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
18