【iOS】线程安全的文件读写

前段时间看了一遍GCD(Grand Central Dispatch)多线程,GCD是苹果为多核开发提供的解决方案

多线程最常见的问题就是读写,比如数据库读写,文件读写,读取是共享的,写是互斥,允许多个线程进行读操作,当写文件时,阻止队列中所有其他的线程进入,直到文件写完成

 

本文利用GCD提供的相关API封装(主要有dispatch_barrier_asyncdispatch_asyncdispatch_queue_create)一个线程安全的文件读写类FileManager

注:这里使用的文件读写使用NSFileManager类,在测试过程发现多个线程同时写文件的时候并没有发现异常和报错,经过一番查阅发现,原来NSFileManager本身就是线程安全的,多个线程对文件进行写操作,并不会报异常

下面代码只作为加深GCD的学习,实际开发如果使用NSFileManager并不需要考虑线程安全问题(使用NSFileManager的delegate的时候需要注意,需要自己定义一个实例维护状态,避免与共享实例冲突)

 

 

SGFileManager.h

@interface SGFileManager : NSObject

+ (instancetype)shareInstance;

- (NSData *)readFile:(NSString *)path;
- (void)readFileAsync:(NSString *)path complete:(void (^)(NSData *data))complete;

- (BOOL)writeFile:(NSString *)path data:(NSData *)data;
- (void)writeFileAsync:(NSString *)path data:(NSData *)data complete:(void (^)(BOOL result))complete;

@end

SGFileManager.m

#import "SGFileManager.h"

//线程队列名称
static char *queueName = "fileManagerQueue";

@interface SGFileManager ()
{
    //读写队列
    dispatch_queue_t _queue;
}

@end


@implementation SGFileManager

+ (instancetype)shareInstance
{
    static id instance = nil;
    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[self alloc] init];
    });
    
    return instance;
}

- (instancetype)init
{
    if(self = [super init]) {
        _queue = dispatch_queue_create(queueName, DISPATCH_QUEUE_CONCURRENT);
    }
    return self;
}

- (NSData *)readFile:(NSString *)path
{
    __block NSData *data;
    dispatch_sync(_queue, ^{
        if([[NSFileManager defaultManager] fileExistsAtPath:path]){
            data = [[NSFileManager defaultManager] contentsAtPath:path];
        }
    });
    return data;
}

- (void)readFileAsync:(NSString *)path complete:(void (^)(NSData *data))complete
{
    dispatch_async(_queue, ^{
        NSData *data = nil;
        
        if([[NSFileManager defaultManager] fileExistsAtPath:path]){
            data = [[NSFileManager defaultManager] contentsAtPath:path];
        }

        if (complete) {
            complete(data);
        }
    });
}

- (BOOL)writeFile:(NSString *)path data:(NSData *)data
{
    __block BOOL result = NO;
    dispatch_barrier_sync(_queue, ^{
        NSFileManager *fileManager = [NSFileManager defaultManager];
        if([fileManager fileExistsAtPath:path]){
            [fileManager removeItemAtPath:path error:nil];
        }
        
        result = [[NSFileManager defaultManager] createFileAtPath:path contents:data attributes:nil];
        
        
        NSLog(@"写文件:");
    });
    return result;
}

- (void)writeFileAsync:(NSString *)path data:(NSData *)data complete:(void (^)(BOOL result))complete
{
    __block BOOL result = NO;
    dispatch_barrier_async(_queue, ^{
        NSFileManager *fileManager = [NSFileManager defaultManager];
        if([fileManager fileExistsAtPath:path]){
            [fileManager removeItemAtPath:path error:nil];
        }
        
        result = [[NSFileManager defaultManager] createFileAtPath:path contents:data attributes:nil];
        
        if (complete) {
            complete(result);
        }
    });
    
}

@end

 

使用:

    NSString *documentRoot = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    NSString *filePath = [documentRoot stringByAppendingPathComponent:@"test.txt"];
    
    [[SGFileManager shareInstance] writeFileAsync:filePath data:data complete:^(BOOL result) {
        if (result) {
            NSLog(@"异步写入文件成功");
        }
    }];
    
    [[SGFileManager shareInstance] readFileAsync:filePath complete:^(NSData *data) {
        if (data) {
            NSLog(@"异步读取文件成功");
        }
    }];

  使用GCD进行多线程开发还是挺方便的,不需要考虑锁的问题,并且性能也比较高,在开发中可以尽量使用GCD进行多线程的开发,并且GCD对从后台到UI的调用也非常方便

posted @ 2015-07-22 20:47  bomo  阅读(10554)  评论(0编辑  收藏  举报