Apple/Classes

NSOperation Code Examples

아침엔커피한잔 2017. 7. 27. 16:07

https://www.objc.io/issues/2-concurrency/common-background-practices/


    
@interface ImportOperation : NSOperation
- (id)initWithStore:(Store*)store fileName:(NSString*)name;
@property (nonatomic) float progress;
@property (nonatomic, copy) void (^progressCallback) (float);
@end

static const int ImportBatchSize = 250;

@interface ImportOperation ()
@property (nonatomic, copy) NSString* fileName;
@property (nonatomic, strong) Store* store;
@property (nonatomic, strong) NSManagedObjectContext* context;
@end

@implementation ImportOperation
{

}

- (id)initWithStore:(Store*)store fileName:(NSString*)name
{
    self = [super init];
    if(self) {
        self.store = store;
        self.fileName = name;
    }
    return self;
}


- (void)main
{
    // TODO: can we use new in the name? I think it's bad style, any ideas for a better name?
    self.context = [self.store newPrivateContext];
    self.context.undoManager = nil;

    [self.context performBlockAndWait:^
    {
        [self import];
    }];
}

- (void)import
{
    NSString* fileContents = [NSString stringWithContentsOfFile:self.fileName encoding:NSUTF8StringEncoding error:NULL];
    NSArray* lines = [fileContents componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
    NSInteger count = lines.count;
    NSInteger progressGranularity = count/100;
    __block NSInteger idx = -1;
    [fileContents enumerateLinesUsingBlock:^(NSString* line, BOOL* shouldStop)
    {
        idx++;
        if (idx == 0) return; // header line

        if(self.isCancelled) {
            *shouldStop = YES;
            return;
        }

        NSArray* components = [line csvComponents];

        if (components.count < 5) {
            NSLog(@"couldn't parse: %@", components);
            return;
        }

        [Stop importCSVComponents:components intoContext:self.context];

        if (idx % progressGranularity == 0) {
            self.progressCallback(idx / (float) count);
        }
        if (idx % ImportBatchSize == 0) {
            [self.context save:NULL];
        }
    }];
    self.progressCallback(1);
    [self.context save:NULL];
}

@end

@interface ImportViewController ()
@property (nonatomic, strong) NSOperationQueue* operationQueue;
@property (nonatomic, strong) FetchedResultsTableDataSource* dataSource;
@end

- (id)init {
    self = [super initWithNibName:@"ImportViewController" bundle:nil];
    if(self) {
        self.operationQueue = [[NSOperationQueue alloc] init];
    }
    return self;
}

- (IBAction)startImport:(id)sender {
    self.progressIndicator.progress = 0;
    NSString* fileName = [[NSBundle mainBundle] pathForResource:@"stops" ofType:@"txt"];
    ImportOperation* operation = [[ImportOperation alloc] initWithStore:self.store fileName:fileName];
    operation.progressCallback = ^(float progress) {
        [[NSOperationQueue mainQueue] addOperationWithBlock:^
        {
            self.progressIndicator.progress = progress;
        }];
    };
    [self.operationQueue addOperation:operation];
}

- (IBAction)cancel:(id)sender {
    [self.operationQueue cancelAllOperations];
}