iOS監(jiān)控/監(jiān)測/監(jiān)聽文件/文件夾的變化 檢測文件變化

本文的原始鏈接在我的博客網(wǎng)站上 https://yohunl.com/iosjian-kong-jian-ce-jian-ting-wen-jian-wen-jian-jia-de-bian-hua-jian-ce-wen-jian-bian-hua/

我們有些時候,需要監(jiān)測一個文件/文件夾的變化,例如在某個文件被修改的時候,可以獲取到通知,或者我們有個播放列表是掃描某個文件夾下的所有文件,那么當(dāng)這個目錄新添或者刪除一些文件后,我們的播放列表要同步更新,這種情況下,我們都需要監(jiān)聽文件/文件夾的變化

本文的demo已經(jīng)上傳到github上了,地址是MonitorFilesChange
Demo的演示效果:

demo演示

Demo的演示說明:
有兩個tab,第一個,是下文的第一種方法,這里用它來監(jiān)控Documents文件夾,右邊的按鈕, 每點擊一次,就創(chuàng)建一個文件到Documents文件夾下,監(jiān)控會收到通知,刷新列表,顯示目錄下的文件名. 第二個tab是第二種方法的演示,演示寫入內(nèi)容到一個文件中,textview展示寫入的內(nèi)容.

由于在UNIX系統(tǒng)中,文件夾和文件其實對系統(tǒng)來說,只是屬性不同而已,地位是一樣的,在下文中,我可能會用文件,也可能會用文件夾,其實都是適用的.

監(jiān)控文件

碰到這樣的一個需求,首先你的反應(yīng)肯定是,這還不簡單,我直接開一個定時器,每隔5s就重新掃描一下指定的文件夾/文件,這不就結(jié)了.....
其實這樣也沒有錯,只是多開了一個定時器,效率低一些而已.但還是解決了問題的,對這種方式,假如文件夾中文件很多,效率就更低了,每次重新拿到列表后,還得做字符串的匹配等等...,這種方式,我就不多說了,任何一個開發(fā)者分分鐘都可以寫出這個低效的代碼.

這是一個常見的需求,官方其實提供了兩種思路來解決這個問題

方法一

官方給出的示例demo Classes_DirectoryWatcher
大體上的思路是:

  1. 根據(jù)文件/文件夾的路徑,調(diào)用open函數(shù)打開文件夾递胧,得到文件句柄俱病。
  2. 通過kqueue()函數(shù)創(chuàng)建一個kqueue隊列來處理系統(tǒng)事件(文件創(chuàng)建或者刪除)凭峡,得到queueId
  3. 創(chuàng)建一個kevent結(jié)構(gòu)體,設(shè)置相關(guān)屬性冲粤,連同kqueue的ID一起傳給kevent()函數(shù),完成系統(tǒng)對kevent的關(guān)聯(lián)页眯。
  4. 調(diào)用CFFileDescriptorCreateRunloopSouce創(chuàng)建一個接收系統(tǒng)事件的runloop source梯捕,同時設(shè)置文件描述符的回調(diào)函數(shù)(回調(diào)函數(shù)采用C語言標(biāo)準(zhǔn)的回調(diào)函數(shù)格式), 并加到默認的runloopMode中窝撵。
  5. 啟用回調(diào)函數(shù)傀顾。
  6. 關(guān)閉kqueue,關(guān)閉文件夾

這樣操作后,當(dāng)文件/文件夾有變化,系統(tǒng)會觸發(fā)相應(yīng)的回調(diào),你收到回調(diào)了,就可以進行各種處理了

其核心代碼如下:

- (void)kqueueFired
{
  int             kq;
  struct kevent   event;
  struct timespec timeout = { 0, 0 };
  int             eventCount;
  
  kq = CFFileDescriptorGetNativeDescriptor(self->kqref);
  assert(kq >= 0);
  
  eventCount = kevent(kq, NULL, 0, &event, 1, &timeout);
  assert( (eventCount >= 0) && (eventCount < 2) );
  
  if (self.fileChangeBlock) {
    self.fileChangeBlock(eventCount);
  }
  
  CFFileDescriptorEnableCallBacks(self->kqref, kCFFileDescriptorReadCallBack);
}

static void KQCallback(CFFileDescriptorRef kqRef, CFOptionFlags callBackTypes, void *info)
{
  MonitorFileChangeUtils *helper = (MonitorFileChangeUtils *)(__bridge id)(CFTypeRef) info;
  [helper kqueueFired];
}

- (void) beginGeneratingDocumentNotificationsInPath: (NSString *) docPath
{
  int                     dirFD;
  int                     kq;
  int                     retVal;
  struct kevent           eventToAdd;
  CFFileDescriptorContext context = { 0, (void *)(__bridge CFTypeRef) self, NULL, NULL, NULL };
  
  dirFD = open([docPath fileSystemRepresentation], O_EVTONLY);
  assert(dirFD >= 0);
  
  kq = kqueue();
  assert(kq >= 0);
  
  eventToAdd.ident  = dirFD;
  eventToAdd.filter = EVFILT_VNODE;
  eventToAdd.flags  = EV_ADD | EV_CLEAR;
  eventToAdd.fflags = NOTE_WRITE;
  eventToAdd.data   = 0;
  eventToAdd.udata  = NULL;
  
  retVal = kevent(kq, &eventToAdd, 1, NULL, 0, NULL);
  assert(retVal == 0);
  
  self->kqref = CFFileDescriptorCreate(NULL, kq, true, KQCallback, &context);
  rls = CFFileDescriptorCreateRunLoopSource(NULL, self->kqref, 0);
  assert(rls != NULL);
  
  CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
  CFRelease(rls);
  CFFileDescriptorEnableCallBacks(self->kqref, kCFFileDescriptorReadCallBack);
}

方法二:GCD方式

大體思路是:
首先是使用O_EVTONLY模式打開文件/文件夾,然后創(chuàng)建一個GCD的source,當(dāng)然了,其unsigned long mask參數(shù)要選VNODE系列的,例如要檢測文件的些人,可以用DISPATCH_VNODE_WRITE

核心代碼:

- (void)__beginMonitoringFile
{
  
  _fileDescriptor = open([[_fileURL path] fileSystemRepresentation],
                         O_EVTONLY);
  dispatch_queue_t defaultQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
  _source = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE,
                                   _fileDescriptor,
                                   DISPATCH_VNODE_ATTRIB | DISPATCH_VNODE_DELETE | DISPATCH_VNODE_EXTEND | DISPATCH_VNODE_LINK | DISPATCH_VNODE_RENAME | DISPATCH_VNODE_REVOKE | DISPATCH_VNODE_WRITE,
                                   defaultQueue);        
  dispatch_source_set_event_handler(_source, ^ {
    unsigned long eventTypes = dispatch_source_get_data(_source);
    [self __alertDelegateOfEvents:eventTypes];
  });
  
  dispatch_source_set_cancel_handler(_source, ^{
    close(_fileDescriptor);
    _fileDescriptor = 0;
    _source = nil;

    // If this dispatch source was canceled because of a rename or delete notification, recreate it
    if (_keepMonitoringFile) {
        _keepMonitoringFile = NO;
        [self __beginMonitoringFile];
    }
  });
  dispatch_resume(_source);
}

參考文檔

handling-filesystem-events-with-gcd
Monitoring-Files-With-GCD-Being-Edited-With-A-Text-Editor
gcd-zhi-jian-ting-wen-jian
GCDWorkQueues
iMonitorMyFiles

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市嫉拐,隨后出現(xiàn)的幾起案子哩都,更是在濱河造成了極大的恐慌,老刑警劉巖婉徘,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異献雅,居然都是意外死亡,警方通過查閱死者的電腦和手機塌计,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門挺身,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人锌仅,你說我怎么就攤上這事∪惹郏” “怎么了?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵府寒,是天一觀的道長报腔。 經(jīng)常有香客問我株搔,道長纯蛾,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任炮姨,我火速辦了婚禮碰煌,結(jié)果婚禮上舒岸,老公的妹妹穿的比我還像新娘芦圾。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布碍脏。 她就那樣靜靜地躺著,像睡著了一般役拴。 火紅的嫁衣襯著肌膚如雪钾埂。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天褥紫,我揣著相機與錄音,去河邊找鬼部念。 笑死,一個胖子當(dāng)著我的面吹牛儡炼,可吹牛的內(nèi)容都是我干的查蓉。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼豌研,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了秆麸?” 一聲冷哼從身側(cè)響起及汉,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤屯烦,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后驻龟,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡类溢,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了闯冷。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡辩诞,死狀恐怖纺涤,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情撩炊,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布伯顶,位于F島的核電站呛踊,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏谭网。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一劫乱、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧衷戈,春花似錦层坠、人聲如沸殖妇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽前鹅。三九已至峭梳,卻和暖如春舰绘,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背口四。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工者蠕, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人踱侣。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像探膊,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子逞壁,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容