Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Мониторинг файлов с поддержкой GCD

Anna | 2.07.2014 | нет комментариев

Думаю, что большинству разработчиков под iOS вестимо как легко включить iTunes File Sharing в своем приложении, добавив лишь одну строчку в Info.plist:

UIFileSharingEnabled = YES

Но это даже не полдела. Соль в том, что, по-отменному, приложение сейчас должно остлеживать все метаморфозы с файлами, протекающие в директории Documents и соответственно обновлять свои данные. Как это релизовать в своём коде и расскажет данная статья.
image

Для начала вовсе немножко теории из Concurrency Programming Guide. В GCD есть такое представление какdispatch source – капитальный тип данных, тот, что ответчает за координацию оброботки специфических низкоуровневых событий. Для решения нашей задачи, нас больше каждого волнует такая его разновидность как descriptor sources, оповещающий о разных операциях с файлами либо сокетами.

Получается, что нам необходимо способом dispatch_source_create сделать диспетчер событий, источником которых послужит дескриптор файлов (directory file descriptor), а по самому событию (запись файла) отработает нужный блок обновления данных приложения.

Выходит, ближе к телу. Сотворим два основных способа startMonitor и stopMonitor, соответственно запускающих и останавлювающи мониторинг требуемой нам директории, а также пару-тройку вспомогательных способов проверки изменений в этой директории, которые будут запускаться через handler block.


- (void)startMonitor
{
	// проверяем сделана ли теснее dispatch_source_t
	if (_src != NULL) return; 

	// путь к директории Documents на устройстве
    NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];

	// дескриптор файлов, только для нотификации событий (O_EVTONLY)
	_fileDescriptor = open([docPath fileSystemRepresentation], O_EVTONLY);

	// трудимся в основной thread, так как будем обновлять UI
	dispatch_queue_t queue = dispatch_get_main_queue();

	// создаем тот самый dispatch source для мониторинга событий (write)
	_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, _fileDescriptor, DISPATCH_VNODE_WRITE,  queue);	

	// handler block отрабатывающий при изменениях в директории
	dispatch_source_set_event_handler(_src, ^{
        [self directoryDidChange];
    });

	// при отмене закрываем дескриптор
	dispatch_source_set_cancel_handler(_src, ^{
      close(_fileDescriptor);
   });

	dispatch_resume(_src);
}

- (void)stopMonitor
{
	if (_src) { // здесь все легко, крушить не строить
		dispatch_source_cancel(_src);
		_src = NULL;
	}
}

- (void)directoryDidChange
{
    if(!waitingForDocumentsDirectoryTimeout) {
        waitingForDocumentsDirectoryTimeout = YES; // включаем флаг ожидания
        _lastDocumentsDirectoryReferenceArray=[self documentsDirectoryReferenceArray]; // получаем массив с файлами в директории
//...и запускаем блок проверки файлов с таймаутом в одну секунду, скажем
        [self performSelector:@selector(checkForDocumentsDirectoryChanges) withObject:nil afterDelay:1.0 inModes:[NSArray arrayWithObjects:NSRunLoopCommonModes,nil]];
    }
}

-(void)checkForDocumentsDirectoryChanges{
    NSArray *newDocumentsDirectoryReferenceArray=[self documentsDirectoryReferenceArray];
    // тривиальный алгорифм сопоставления 2-х массивов
    if(![newDocumentsDirectoryReferenceArray isEqualToArray:_lastDocumentsDirectoryReferenceArray]) {
      // рекурсивно продолжаем проверку файлов с таймаутом
        _lastDocumentsDirectoryReferenceArray=newDocumentsDirectoryReferenceArray;
        [self performSelector:@selector(checkForDocumentsDirectoryChanges) withObject:nil afterDelay:1.0 inModes:[NSArray arrayWithObjects:NSRunLoopCommonModes,nil]];
    } else {
        // синхронизация файлов закончена
        waitingForDocumentsDirectoryTimeout=NO;
        _lastDocumentsDirectoryReferenceArray=nil;
       // ...здесь уж необходимо вставить ваш блок обновления данных в программе, скажем
      [self scanDocumentsDerectory];
    }
}

-(NSArray *)documentsDirectoryReferenceArray {
    // возвращает массив со списком файлов в директории формата Наименование-Размер
    NSString *documentsDirectoryPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    NSArray *documentsDirectoryContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:documentsDirectoryPath error:NULL];

    NSMutableArray *documentsDirectoryReferenceArray=[NSMutableArray arrayWithCapacity:10];

    for(NSString *fileName in documentsDirectoryContents){

        NSString *filePath=[documentsDirectoryPath stringByAppendingPathComponent:fileName];

        NSError *error;
        NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:&error];
        NSInteger fileSize = [[fileAttributes objectForKey:NSFileSize] intValue];
        NSString *fileWithLength=[NSString stringWithFormat:@"%@-%d",fileName,fileSize];

        [documentsDirectoryReferenceArray addObject:fileWithLength];
    }

    return documentsDirectoryReferenceArray;
}

Ну, а для тех кто пробежался глазами по тектсу и кому лень разбираться, сходственный подход теснее реализован в Cocoanetics/DTFoundation класс DTFolderMonitor.

Еще, из навыка работы над своим приложением, использующем iTunes File Sharing, хочу напомнить о необходимости запуска способа типа scanDocumentDerectory, помимо мониторинга, как только приложение становится энергичным. Его предназначение не только проверять, но и обновлять данные о файлах в приложении с их фактическим наличием в директории, так как синхронизация файлов с iTunes может протекать и в момент, когда приложение находится в бэкграунде, либо вовсе не запущено.


- (void)applicationWillEnterForeground:(UIApplication *)application
{
    [self scanDocumentsDirectory];
}

Ссылки на первоисточники

Историческая ветка обсуждений по теме на форуме разработчиков Apple (нужен девелоперский акаунт для доступа)
Directory Monitor – олдскульный монитор от Michael Heyeck
Directory Monitoring and GCD он же, но обновленный
Monitoring a Folder with GCD решение от Сocoanetics

 

Источник: programmingmaster.ru

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB