Вопрос:

closeWithCompletionHandler имеет странные результаты при выполнении из фонового потока

ios objective-c multithreading uidocument

35 просмотра

1 ответ

129 Репутация автора

У меня проблемы с какой-то пакетной обработкой документов в iOS, и я надеялся, что мне здесь помогут. Процесс, который я пытаюсь реализовать, использует входной каталог на диске iCloud, извлекает все документы и добавляет по одной записи в базу данных iCloud для каждого. Прямо сейчас, код «добавить iCloud» не здесь, но это достаточно легко сделать, если я пропущу правильные указатели вокруг. Я хотел бы дать пользователю индикатор выполнения, чтобы он мог проверить обработку и понять, насколько хорошо она идет. Эту технику я часто использую в macOS и просто предполагал, что в iOS она будет работать нормально, но у меня возникли трудности.

Теперь странная часть этого заключается в том, что если я удаляю код, который запускает цикл в фоновом потоке, и запускаю класс загрузчика в основном потоке, процесс более или менее работает. Индикатор выполнения, конечно, не работает (он никогда не возвращает основной поток), но код открытия документа вызывается правильное количество раз, и создаются процессы, чтобы открыть документы и все такое. Когда я запускаю его как есть, я получаю аварийное завершение, которое, похоже, указывает на то, что процесс выделения или освобождения нарушен. На самом деле не получайте крах, только разрыв, который постоянно повторяется.

Основной процесс заключается в следующем:

Пользователь выбирает опцию загрузки и целевой каталог на экране и нажимает «идти». Как часть segue, процесс загрузчика создается и ждет, пока немного начнется (есть лучшие способы сделать это, но они могут подождать, пока я не соберу базовый процесс)

Пользователь переходит на другой экран, на котором отображается индикатор выполнения. Вот код для этого:

//
//  LoadPopover.m
//  TestFindIt
//
//  Created by Joe Ruth on 7/16/16.
//  Copyright © 2016 Joe Ruth. All rights reserved.  
//

#import "LoadPopover.h"

@interface LoadPopover ()
@end

@implementation LoadPopover;

@synthesize localSecondViewController, localLoadCloudDBClass, loadProgressBar,     screenReloadDirectory;
@synthesize loadedDocuments;

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (IBAction)pressedDoneButton:(id)sender {

    [self dismissViewControllerAnimated:YES completion:nil];
}


-(void)viewDidAppear:(BOOL)animated {

    [localLoadCloudDBClass addObserver:self
                             forKeyPath:@"iterationcounter"
                                options:NSKeyValueObservingOptionNew
                                context:NULL];

    [localLoadCloudDBClass addObserver:self
                             forKeyPath:@"totalcounter"
                                options:NSKeyValueObservingOptionNew
                                context:NULL];

    loadProgressBar.progress = 0.00;

    self.loadedDocuments = [[NSMutableArray alloc] init];
    localLoadCloudDBClass.loadedDocuments = self.loadedDocuments;

    [localLoadCloudDBClass LoadCloudDBClassRun:self];

}


- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)objectchange:(NSDictionary *)changecontext:(void *)context   
    {

        [[NSOperationQueue mainQueue] addOperationWithBlock:^{


        int iterationcounter;
        int totalcounter;

        iterationcounter = (int) localLoadCloudDBClass.iterationcounter;
        totalcounter = (int) localLoadCloudDBClass.totalcounter;

        if (totalcounter != 0) {
            loadProgressBar.progress = (float) iterationcounter / (float) totalcounter;}
        else {
            loadProgressBar.progress = 0.00;}

    }];
}

@end

Экран перехода устанавливает наблюдателя для некоторых свойств счетчика для класса загрузчика.

Загрузчик довольно прост. Прямо сейчас это просто просмотр страниц базы данных iCloud и запуск процесса открытия документа для каждой записи в базе данных. В какой-то момент мне понадобится немного увлечься процессом открытия документа, но сейчас это доказательство концепции.

//
//  LoadCloudDBClass.m
//  TestFindIt
//
//  Created by Joe Ruth on 7/15/16.
//  Copyright © 2016 Joe Ruth. All rights reserved.  
//

#import "LoadCloudDBClass.h"
#import "Item.h"
#import "SaveObject.h"
#import "SaveObjectUIDocument.h"

@implementation LoadCloudDBClass

@synthesize localFindItDataController, screenReloadDirectory, localLoadPopover, iterationcounter, totalcounter;
@synthesize loadedDocuments;

- (void) LoadCloudDBClassRun:(NSObject *) parameterTestFinditCaller {

    dispatch_queue_t backgroundQueue = dispatch_queue_create("Background Queue",NULL);

    dispatch_async(backgroundQueue, ^{

        NSError *error;
        unsigned long check_count;
        unsigned long total_count;
        NSURL *fileURL;
        int stopper;

        __block BOOL blockSuccess = NO;
        __block BOOL blockCalled = NO;

        [self setIterationcounter:(NSInteger) 0];
        [self setTotalcounter:(NSInteger) 0];

        NSFileManager *fm = [NSFileManager defaultManager];
        NSURL *rootURL = [fm URLForUbiquityContainerIdentifier:nil];
        NSURL *newFolderTemp = [rootURL URLByAppendingPathComponent:@"Documents" isDirectory:YES];
        NSURL *newFolder = [newFolderTemp URLByAppendingPathComponent:screenReloadDirectory isDirectory:YES];

        NSNumber *isDirectory;
        if (![newFolder getResourceValue:&isDirectory forKey:NSURLIsDirectoryKey error:&error]) {return;}

        NSArray *theFiles =  [fm contentsOfDirectoryAtURL:newFolder
                               includingPropertiesForKeys:[NSArray arrayWithObject:NSURLNameKey]
                                                  options:NSDirectoryEnumerationSkipsHiddenFiles
                                                error:nil];

       [self setTotalcounter:(NSInteger) [theFiles count]];

        total_count = [theFiles count];
        check_count = 0;

        while ((check_count < total_count) && (total_count != 0)) {

            fileURL = [theFiles objectAtIndex:check_count];

            SaveObjectUIDocument *tempdoc = [[SaveObjectUIDocument alloc] initWithFileURL:fileURL];
            //tempdoc.loadedDocuments = loadedDocuments;

            long set_iterationcounter = iterationcounter + 1;
            [self setIterationcounter:(NSInteger) set_iterationcounter];

            [tempdoc openWithCompletionHandler:^(BOOL success) {
                 NSLog (@" try Open");
                 if (success) {
                    blockSuccess = success;
                    blockCalled = YES;
                    //[self.loadedDocuments addObject:tempdoc];
                    NSLog(@"Opened");}
                else
                    {NSLog(@"Not Opened");}

            }];

            blockCalled = NO;

            NSDate *loopUntil = [NSDate dateWithTimeIntervalSinceNow:10];
            while (!blockCalled && [loopUntil timeIntervalSinceNow] > 0)
            {
                [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
                                         beforeDate:loopUntil];
            }

            check_count++;

        }

        stopper=5;

    });


}

@end

Кодировка документа выглядит следующим образом:

Вот файл .h

//
//  SaveObject.h
//  TestFindIt
//
//  Created by Joe Ruth on 1/10/16.
//  Copyright © 2016 Joe Ruth. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface SaveObject : NSObject <NSCoding> {

    NSData *_sxmlData;

}

- (id)initWithData:(NSData *)in_xmlData;

@property (nonatomic, copy) NSData *sxmlData;

@end

Вот сопроводительный файл .m

//
//  SaveObject.m
//  TestFindIt
//
//  Created by Joe Ruth on 1/10/16.
//  Copyright © 2016 Joe Ruth. All rights reserved.
//

#import "SaveObject.h"

@implementation SaveObject

@synthesize sxmlData = _sxmlData;

- (id)initWithData:(NSData *)in_xmlData {
    if ((self = [super init])) {
        self.sxmlData = in_xmlData;


   }
    return self;
}

- (id)init {
    return [self initWithData:nil];
}

#pragma mark NSCoding

#define kVersionKey @"Version"
#define kDataKey @"Data"
#define kSaveObjectXMLData @"SaveObjectXMLData"

//- (void)encodeWithCoder:(NSCoder *)encoder {
//    [encoder encodeInt:1 forKey:kVersionKey];
//    [encoder encodeObject:self.sxmlData forKey:kDataKey];
//}

//- (id)initWithCoder:(NSCoder *)decoder {
//    [decoder decodeIntForKey:kVersionKey];
//    NSData * outxmlData = [decoder decodeObjectForKey:kDataKey];
//    NSLog(@">>>>>>>>>>>>>>>>>>> %@",outxmlData);
//    return [self initWithData:outxmlData];
//}

#pragma mark -

#pragma mark NSCoding Protocol

- (void)encodeWithCoder:(NSCoder *)coder {
    [coder encodeObject:self.sxmlData forKey:kSaveObjectXMLData];
}

- (id)initWithCoder:(NSCoder *)coder  {
    self = [super init];

    if (self != nil) {


       self.sxmlData = [coder decodeObjectForKey:kSaveObjectXMLData];
    }

    return self;
}

@end

Вот высокоуровневый документ кодирования .h файл

//
//  SaveObjectUIDocument.h
//  TestFindIt
//
//  Created by Joe Ruth on 1/10/16.
//  Copyright © 2016 Joe Ruth. All rights reserved.
//

#import <UIKit/UIKit.h>

@class SaveObject;


@interface SaveObjectUIDocument : UIDocument {

    SaveObject *_SaveObject;

}

@property (strong, nonatomic) SaveObject *SaveObject;

@конец

И сопроводительный файл .m. Позже сюда будет добавлен код добавления iCloud, и мне нужно убедиться, что адрес открытого объекта MOC был правильно передан.

//
//  Created by Joe Ruth on 1/10/16.
//  Copyright © 2016 Joe Ruth. All rights reserved.
//

#import "SaveObjectUIDocument.h"
#import "SaveObject.h"

#define kArchiveKey @"SaveObjectXMLData"

@interface SaveObjectUIDocument ()
@property (nonatomic, strong) NSData * data;
@end

@implementation SaveObjectUIDocument;

@synthesize SaveObject = _SaveObject;

- (BOOL)loadFromContents:(id)contents ofType:(NSString *)typeName error:(NSError *__autoreleasing *)outError {
    if ([contents length] > 0) {
        NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:contents];
        self.SaveObject = [unarchiver decodeObjectForKey:kArchiveKey];
        [unarchiver finishDecoding];}
    else {
        self.SaveObject = [[SaveObject alloc] initWithData:nil];}
    return YES;
}


- (id)contentsForType:(NSString *)typeName error:(NSError *__autoreleasing *)outError {
    NSMutableData *data = [[NSMutableData alloc] init];
    NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
    [archiver encodeObject:self.SaveObject forKey:kArchiveKey];
    [archiver finishEncoding];

    return data;

}

- (void)handleError:(NSError *)error userInteractionPermitted:(BOOL)userInteractionPermitted {
    NSLog(@"Error: %@ userInfo=%@", error.localizedDescription, error.userInfo);
    [super handleError:error userInteractionPermitted:userInteractionPermitted];
}


@end
Автор: joseph ruth Источник Размещён: 22.08.2016 08:23

Ответы (1)


0 плюса

129 Репутация автора

Получил самостоятельный ответ за это. Вынудил openWithCompletionHandler: обработать основной поток, и он заработал. Пойди разберись.

Автор: joseph ruth Размещён: 01.09.2016 03:21
Вопросы из категории :
32x32