【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的調用也非常方便


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM