iOS 數(shù)據(jù)持久化詳解

文章來源:愛聽音樂的狗博客

數(shù)據(jù)持久化方式:

1、屬性列表(plist存儲)

2、偏好設(shè)置(NSUserDefaults)

3、歸檔序列化存儲

4、沙盒存儲

5、Core Data

6、SQLite3

7、FMDB

8、Realm

應(yīng)用場景及使用

1、屬性列表(plist存儲)
通常叫做plist文件,用于存儲在程序中不經(jīng)常修改、數(shù)據(jù)量小的數(shù)據(jù),不支持自定義對象存儲,支持?jǐn)?shù)據(jù)存儲的類型為:Array,Dictionary,String,Number,Data,Date,Boolean,通常用來存放接口名、城市名、銀行名稱、表情名等極少修改的數(shù)據(jù)

1.1 創(chuàng)建plist文件并添加數(shù)據(jù)

右擊文件目錄 ---> 選擇"New File..." --->選擇"Property List" ---> 輸入plist文件名并在窗口中點(diǎn)擊Create創(chuàng)建。在創(chuàng)建好的plist文件中選擇Root類型并添加測試數(shù)據(jù),如圖所示:

創(chuàng)建.png
添加測試數(shù)據(jù).png

1.2 獲取plist文件數(shù)據(jù)

文件的類型為Array就使用數(shù)組獲取,類型為Dictionary就使用字典方式獲取數(shù)據(jù),示例代碼:

/**
 plist獲取數(shù)據(jù)
 
 @param sender sender description
 */
- (IBAction)plistGetInfoButtonDidClicked:(UIButton *)sender {
    //獲取plist文件路徑
    NSString *path = [self getPlistFilePath];
    NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:path];
    NSLog(@"獲取到的plist數(shù)據(jù)\n%@",dict);
}

/**
 獲取plist文件路徑

 @return 路徑
 */
- (NSString *)getPlistFilePath {
    //獲取plist文件路徑
    NSString *path = [[NSBundle mainBundle] pathForResource:@"testProperty" ofType:@"plist"];
    return path;
}

2 偏好設(shè)置(NSUserDefaults)

用于存儲用戶的偏好設(shè)置,同樣適合于存儲輕量級的用戶數(shù)據(jù),數(shù)據(jù)會自動保存在沙盒的Libarary/Preferences目錄下,本質(zhì)上就是一個plist文件,所以同樣的不支持自定義對象存儲,支持?jǐn)?shù)據(jù)存儲的類型為:Array,Dictionary,String,Number,Data,Date,Boolean,可以用做檢查版本是否更新、是否啟動引導(dǎo)頁、自動登錄、版本號等等,需要注意的是NSUserDefaults是定時的將緩存中的數(shù)據(jù)寫入磁盤,并不是即時寫入,為了防止在寫完NSUserDefaults后,程序退出導(dǎo)致數(shù)據(jù)的丟失,可以在寫入數(shù)據(jù)后使用synchronize強(qiáng)制立即將數(shù)據(jù)寫入磁盤

2.1 為NSUserDefaults創(chuàng)建分類

右擊文件目錄 ---> 選擇"New File..." --->選擇"Objective-C File" --->選擇File Type方式、類并且輸入名稱

創(chuàng)建分類2.png

2.2 保存、獲取數(shù)據(jù)示例:

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface NSUserDefaults (Category)


/**
 保存電話號碼
 */
+ (void)savePhoneNumber:(NSString *)phoneNumber;

/**
 獲取電話號碼

 @return 電話號碼
 */
+ (NSString *)getPhoneNumber;
@end

NS_ASSUME_NONNULL_END

#import "NSUserDefaults+Category.h"

static NSString * const phoneNumberKey = @"phoneNumberKey";

@implementation NSUserDefaults (Category)

#pragma mark - 電話號碼
+ (void)savePhoneNumber:(NSString *)phoneNumber {
    [[NSUserDefaults standardUserDefaults] setObject:phoneNumber forKey:phoneNumberKey];
    [[NSUserDefaults standardUserDefaults] synchronize];
}
+ (NSString *)getPhoneNumber {
   return [[NSUserDefaults standardUserDefaults] objectForKey:phoneNumberKey];
}

3 歸檔序列化存儲

歸檔可以直接將對象存儲為文件,也可將文件直接解歸檔為對象,相對于plist文件與偏好設(shè)置數(shù)據(jù)的存儲更加多樣,支持自定義的對象存儲,歸檔后的文件是加密的,也更加的安全,文件存儲的位置可以自定義。

3.1 對象支持歸檔與解歸檔

創(chuàng)建HJPersonModel類繼承自NSObject ,遵守NSCoding或者NSSecureCoding協(xié)議,重寫兩個方法。"supportsSecureCoding"是NSSecureCoding協(xié)議下的重要屬性,如果采用NSSecureCoding協(xié)議,必須重寫supportsSecureCoding 方法并返回YES

#import "HJPersonModel.h"

@interface HJPersonModel () <NSSecureCoding>

@end

@implementation HJPersonModel

- (void)encodeWithCoder:(nonnull NSCoder *)aCoder {
    [aCoder encodeObject:_name forKey:@"name"];
    [aCoder encodeObject:@(_age) forKey:@"age"];
}

- (nullable instancetype)initWithCoder:(nonnull NSCoder *)aDecoder {
    if (self = [super init]) {
        self.name =  [aDecoder decodeObjectForKey:@"name"];
        self.age = [[aDecoder decodeObjectForKey:@"age"] integerValue];
    }
    return self;
}

+ (BOOL)supportsSecureCoding {
    return YES;
}

3.2 創(chuàng)建歸檔解歸檔工具

創(chuàng)建HJArchiveTool工具類繼承自NSObject ,提供兩個方法以實(shí)現(xiàn)歸檔解歸檔

#import "HJArchiveTool.h"

@implementation HJArchiveTool

#pragma mark - 歸檔解歸檔

+ (BOOL)archiveObject:(id)object prefix:(NSString *)prefix {
    if (!object)
        return NO;
    NSError *error;
    //會調(diào)用對象的encodeWithCoder方法
    NSData *data = [NSKeyedArchiver archivedDataWithRootObject:object
                          requiringSecureCoding:YES
                                          error:&error];
    if (error)
        return NO;
    
    [data writeToFile:[self getPathWithPrefix:prefix] atomically:YES];
    return YES;
}

+ (id)unarchiveClass:(Class)class prefix:(NSString *)prefix {
    
    NSError *error;
    NSData *data = [[NSData alloc] initWithContentsOfFile:[self getPathWithPrefix:prefix]];
    //會調(diào)用對象的initWithCoder方法
    id content = [NSKeyedUnarchiver unarchivedObjectOfClass:class fromData:data error:&error];
    if (error) {
        return nil;
    }
    return content;
}


/**
 存放的文件路徑

 @return return value description
 */
+ (NSString *)getPathWithPrefix:(NSString *)prefix {
    
    NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
    NSString *filePathFolder = [documentPath stringsByAppendingPaths:@[@"archiveTemp"]].firstObject;
    if (![[NSFileManager defaultManager] fileExistsAtPath:filePathFolder]) {
        [[NSFileManager defaultManager] createDirectoryAtPath:filePathFolder withIntermediateDirectories:YES attributes:nil error:nil];
    }
    NSString *path = [NSString stringWithFormat:@"%@/%@.archive",filePathFolder,prefix];
    return path;
}

3.3 歸檔與解歸檔對象

HJPersonModel類對應(yīng)的數(shù)據(jù)歸檔/解歸檔操作

#pragma mark - 歸檔解歸檔
/**
歸檔數(shù)據(jù)

@param sender sender description
*/
- (IBAction)archiveButtonDidClicked:(UIButton *)sender {
   HJPersonModel *model = [HJPersonModel new];
   model.name = @"小李子";
   model.age = 25;
   if ([HJArchiveTool archiveObject:model prefix:NSStringFromClass(model.class)]) {
       NSLog(@"歸檔成功");
   } else {
       NSLog(@"歸檔失敗");
   }
}
/**
解歸檔數(shù)據(jù)

@param sender sender description
*/
- (IBAction)unarchiveButtonDidClicked:(UIButton *)sender {
   id content = [HJArchiveTool unarchiveClass:HJPersonModel.class prefix:NSStringFromClass(HJPersonModel.class)];
   NSLog(@"%@",content);
}

4 沙盒存儲

可以提高程序的體驗(yàn)度,為用戶節(jié)約數(shù)據(jù)流量,主要在用戶閱讀書籍、聽音樂、看視頻等,在沙盒中做數(shù)據(jù)的存儲,主要包含文件夾:
Documents: 最常用的目錄,存放重要的數(shù)據(jù),iTunes同步時會備份該目錄
Library/Caches: 一般存放體積大,不重要的數(shù)據(jù),iTunes同步時不會備份該目錄
Library/Preferences: 存放用戶的偏好設(shè)置,iTunes同步時會備份該目錄
tmp: 用于存放臨時文件,在程序未運(yùn)行時可能會刪除該文件夾中的數(shù)據(jù),iTunes同步時不會備份該目錄

存放音樂文件示例:

4.1 創(chuàng)建HJSandBoxOperationTool工具類繼承自NSObject,提供"保存音樂到沙盒路徑"和"獲取沙盒路徑下的音樂"兩個方法

#import "HJSandBoxOperationTool.h"


@implementation HJSandBoxOperationTool


/**
 保存音樂到沙盒路徑

 @param musicUrlStr url地址
 */
+ (void)toolToSaveMusicToCache:(NSString *)musicUrlStr withMusicName:(NSString *)name {
    __weak typeof(self) weakSelf = self;
    dispatch_async(dispatch_queue_create("musicQueue", DISPATCH_QUEUE_PRIORITY_DEFAULT), ^{
        //已經(jīng)存了
        if ([weakSelf alreadySaveMusic:name])
            return ;
        NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:musicUrlStr]];
        [data writeToFile:[weakSelf getPathWithMusicName:name] atomically:YES];
    });
}

/**
 獲取沙盒路徑下的音樂
 
 @param musicName 音樂名稱
 @return 路徑
 */
+ (NSString *)toolToGetMusicFromCache:(NSString *)musicName {
    NSString *musicPath = [self getPathWithMusicName:musicName];
    if ([[NSFileManager defaultManager] fileExistsAtPath:musicPath]) {
        return musicPath;
    }
    return nil;
}

/**
 獲取沙盒路徑下的音樂
 
 @return return value description
 */
+ (NSString *)getPathWithMusicName:(NSString *)name {
    
    NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
    NSString *filePathFolder = [documentPath stringsByAppendingPaths:@[@"music"]].firstObject;
    if (![[NSFileManager defaultManager] fileExistsAtPath:filePathFolder]) {
        [[NSFileManager defaultManager] createDirectoryAtPath:filePathFolder withIntermediateDirectories:YES attributes:nil error:nil];
    }
    NSString *path = [NSString stringWithFormat:@"%@/%@.mp3",filePathFolder,name];
    return path;
}


/**
 判斷是否已經(jīng)存儲了音樂

 @param musicName 音樂名稱
 @return return value description
 */
+ (BOOL)alreadySaveMusic:(NSString *)musicName {
    NSString *musicPath = [self getPathWithMusicName:musicName];
    if (![[NSFileManager defaultManager] fileExistsAtPath:musicPath]) {
        return NO;
    } else {
        NSData *data = [NSData dataWithContentsOfFile:musicPath];
        if (data.length > 0) {
            return YES;
        }
        return NO;
    }
}

4.2 工具類方法調(diào)用

#pragma mark - 沙盒存儲

/**
 沙盒存數(shù)據(jù)

 @param sender sender description
 */
- (IBAction)sandBoxSaveButtonDidClicked:(UIButton *)sender {
    //musicUrl 為.mp3格式的音樂url地址
    [HJSandBoxOperationTool toolToSaveMusicToCache:musicUrl withMusicName:@"耳朵"];
}

/**
 沙盒取數(shù)據(jù)

 @param sender sender description
 */
- (IBAction)sendBoxGetInfoButtonDidClicked:(UIButton *)sender {
    NSString *str = [HJSandBoxOperationTool toolToGetMusicFromCache:@"耳朵"];
    NSLog(@"%@",str);
}

5 Core Data

Core Data是框架,并不是數(shù)據(jù)庫,該框架提供了對象關(guān)系的映射功能,使得能夠?qū)C對象轉(zhuǎn)換成數(shù)據(jù),將數(shù)據(jù)庫中的數(shù)據(jù)還原成OC對象,在轉(zhuǎn)換的過程中不需要編寫任何的SQL語句,在Core Data中有三個重要的概念:

NSPersistentStoreCoordinator:持久化存儲協(xié)調(diào)器,在NSPersistentStoreCoordinator中包含了持久化存儲區(qū),在持久化存儲區(qū)中包含了數(shù)據(jù)表中的很多數(shù)據(jù),持久化存儲區(qū)的設(shè)置通常選擇NSSQLiteStoreType,也就是選擇SQLite數(shù)據(jù)庫

NSManagedObjectModel:托管對象模型,用于描述數(shù)據(jù)結(jié)構(gòu)的模型

NSManagedObjectContext:托管對象上下文,在上下文中包含多個托管對象,用于管理對象的生命周期,為何會出現(xiàn)NSManagedObjectContext,原因在于將對象轉(zhuǎn)換成數(shù)據(jù)是保存到磁盤上的,磁盤與RAM之間傳輸數(shù)據(jù)是會有開銷,并且磁盤讀寫的速度沒有RAM快,通過托管對象上下文可以迅速的獲取到數(shù)據(jù),修改了數(shù)據(jù)之后,需要調(diào)用上下文的save方法,將數(shù)據(jù)寫入到數(shù)據(jù)庫中,也就是磁盤中

5.1 Core Data 文件的簡單創(chuàng)建

右擊文件夾 ---> 選擇 "New File..." ---> 選擇"Data Model" ---> 輸入文件名稱并創(chuàng)建。創(chuàng)建后綴名為.xcdatamodeld的模型文件后,添加實(shí)體,并在實(shí)體中添加屬性及類別

創(chuàng)建Core Data文件.png

點(diǎn)擊"Editor"菜單項(xiàng) ---> 選擇"Create NSManagedObject Subclass..."項(xiàng)--->選擇創(chuàng)建的Data Model--->選擇創(chuàng)建的實(shí)體類--->選擇需要放置的文件目錄--->點(diǎn)擊"Create"創(chuàng)建托管對象類。

構(gòu)建實(shí)體類.png

5.2 單利實(shí)例化CoreDataManager

創(chuàng)建HJCoreDataManager繼承自NSObject,針對Core Data的基本操作進(jìn)行封裝。.h文件提供單利創(chuàng)建與刪除數(shù)據(jù)庫兩個方法,.m文件為方法的實(shí)現(xiàn)

#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>


NS_ASSUME_NONNULL_BEGIN

@interface HJCoreDataManager : NSObject


/**
 單利創(chuàng)建

 @return HJCoreDataManager
 */
+ (instancetype)sharedInstanceManager;

/**
 刪除數(shù)據(jù)庫
 */
+ (void)managerForDelete;

//......

@end

NS_ASSUME_NONNULL_END
#import "HJCoreDataManager.h"

#define HJStrIsEmpty(str) ((str == nil) || ([str isEqualToString: @""]) || (str == NULL) ||     ([str isKindOfClass:[NSNull class]]))


@interface HJCoreDataManager ()


/**
 數(shù)據(jù)模型
 */
@property (nonatomic, strong) NSManagedObjectModel *objectModel;


/**
 持久化數(shù)據(jù)
 */
@property (nonatomic, strong) NSPersistentStoreCoordinator *coordinator;

/**
 管理數(shù)據(jù)的對象
 */
@property (nonatomic, strong) NSManagedObjectContext *objectContext;
@end

@implementation HJCoreDataManager

#pragma mark - 單利創(chuàng)建
+ (instancetype)sharedInstanceManager {
    static HJCoreDataManager *manager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        manager = [[HJCoreDataManager alloc] init];
    });
    return manager;
}

- (instancetype)init {
    if (self = [super init]) {
        //創(chuàng)建托管對象模型
        NSURL *url = [[NSBundle mainBundle] URLForResource:@"HJCoreData" withExtension:@"momd"];
        _objectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:url];
        //創(chuàng)建持久化數(shù)據(jù)協(xié)調(diào)器
        _coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:_objectModel];
        //關(guān)聯(lián)并創(chuàng)建本地數(shù)據(jù)庫文件
        [_coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:[self dbPath] options:nil error:nil];
        //創(chuàng)建托管對象上下文
        _objectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:(NSMainQueueConcurrencyType)];
        [_objectContext setPersistentStoreCoordinator:_coordinator];
    }
    return self;
}


/**
 獲取數(shù)據(jù)庫路徑

 @return return value description
 */
- (NSURL *)dbPath {
    NSString *documentPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject;
    NSString *dbFolder = [documentPath stringByAppendingPathComponent:@"CoreData"];
    if (![[NSFileManager defaultManager] fileExistsAtPath:dbFolder]) {
        [[NSFileManager defaultManager] createDirectoryAtPath:dbFolder withIntermediateDirectories:YES attributes:nil error:nil];
    }
    NSURL *dbUrl = [[NSURL fileURLWithPath:dbFolder] URLByAppendingPathComponent:@"db.sqlite"];
    return dbUrl;
}

#pragma mark - 刪除數(shù)據(jù)庫
+ (void)managerForDelete {
    NSString *documentPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
    NSString *filePath = [documentPath stringByAppendingPathComponent:@"CoreData"];
    if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
        [[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
    }
}

//......

@end

5.3 數(shù)據(jù)的增刪改查

數(shù)據(jù)增刪改查操作的基本封裝,.h文件提供方法,.m文件實(shí)現(xiàn)方法,需要注意的是,在對數(shù)據(jù)進(jìn)行了修改之后都要調(diào)用上下文的save方法,將數(shù)據(jù)寫入到數(shù)據(jù)庫中,也就是磁盤中

//================================ 添加數(shù)據(jù) ================================//

/**
 獲取數(shù)據(jù)模型

 @param entityName 數(shù)據(jù)模型類名稱
 @return return value description
 */
+ (NSManagedObject *)getTableWithEntityName:(NSString * _Nonnull)entityName;

/**
 保存數(shù)據(jù)
 */
+ (BOOL)save;

//================================       ================================//

/**
 刪除數(shù)據(jù)

 @param entityName   數(shù)據(jù)模型類名稱
 @param searchString 屬性名的值包含的字符
 @param attribute    屬性名稱
 @return             成功失敗
 */
+ (BOOL)deleteByEntityName:(NSString * _Nonnull)entityName
               withMaching:(NSString * _Nonnull)searchString
             withAttribute:(NSString * _Nonnull)attribute;

/**
 更新數(shù)據(jù)

 @param managedObject pojo對象
 @return bool
 */
+ (BOOL)updateManagedObject:(NSManagedObject *)managedObject;

/**
 查詢數(shù)據(jù)

 @param entityName   數(shù)據(jù)模型類名稱
 @param searchString 屬性名的值包含的字符
 @param attribute    屬性名稱
 @param sortArribute 按哪個屬性名稱排序
 @param ascending    是否升序
 @return             模型數(shù)組
 */
+ (NSArray *)selectByEntityName:(NSString * _Nonnull)entityName
                    withMaching:(NSString * _Nullable)searchString
                  withAttribute:(NSString * _Nullable)attribute
                      sortingBy:(NSString * _Nullable)sortArribute
                    isAscending:(BOOL)ascending;
#pragma mark - 獲取數(shù)據(jù)模型
+ (NSManagedObject *)getTableWithEntityName:(NSString *)entityName {
    NSManagedObject *managedObject = [NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:[HJCoreDataManager sharedInstanceManager].objectContext];
    return managedObject;
}

#pragma mark - 保存數(shù)據(jù)
+ (BOOL)save {
    NSError *error;
    BOOL success = [[HJCoreDataManager sharedInstanceManager].objectContext save:&error];
    return success;
}

#pragma mark - 刪除數(shù)據(jù)
+ (BOOL)deleteByEntityName:(NSString * _Nonnull)entityName
               withMaching:(NSString * _Nonnull)searchString
             withAttribute:(NSString * _Nonnull)attribute {
    //沒有輸入刪除條件
    if (HJStrIsEmpty(attribute) || HJStrIsEmpty(searchString)) {
        return YES;
    }
    //查詢數(shù)據(jù)
    NSArray *array = [self selectByEntityName:entityName
                                  withMaching:searchString
                                withAttribute:attribute
                                    sortingBy:attribute
                                  isAscending:YES];
    if (array.count > 0) {
        //刪除
        for (NSManagedObject *object in array) {
            [[HJCoreDataManager sharedInstanceManager].objectContext deleteObject:object];
        }
    }
    //執(zhí)行保存操作
    return [HJCoreDataManager save];
}

#pragma mark - 更新數(shù)據(jù)
+ (BOOL)updateManagedObject:(NSManagedObject *)managedObject {
    [[HJCoreDataManager sharedInstanceManager].objectContext refreshObject:managedObject mergeChanges:YES];
    return [HJCoreDataManager save];
}

#pragma mark - 查詢數(shù)據(jù)
+ (NSArray *)selectByEntityName:(NSString * _Nonnull)entityName
                    withMaching:(NSString * _Nullable)searchString
                  withAttribute:(NSString * _Nullable)attribute
                      sortingBy:(NSString * _Nullable)sortArribute
                    isAscending:(BOOL)ascending{
    NSEntityDescription *entity = [NSEntityDescription entityForName:entityName inManagedObjectContext:[HJCoreDataManager sharedInstanceManager].objectContext];
    //創(chuàng)建fetch請求
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    fetchRequest.entity = entity;
    //一次性獲取完
    [fetchRequest setFetchBatchSize:0];
    if (!HJStrIsEmpty(sortArribute)) {
        //排序
        NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:sortArribute ascending:ascending selector:nil];
        NSArray *descriptors = @[sortDescriptor];
        fetchRequest.sortDescriptors = descriptors;
    } else {
        fetchRequest.sortDescriptors = @[];
    }
    
    if (!HJStrIsEmpty(searchString) && !HJStrIsEmpty(attribute)) {
        //某個屬性的值包含某個字符串
        //%K 某個屬性的值
        //%@ 傳遞過來的字符串
        //模糊查詢 contains[cd] 包含某個值 c標(biāo)識忽略大小寫,d標(biāo)識忽略重音
        //查詢 ==
        fetchRequest.predicate = [NSPredicate predicateWithFormat:@"%K == %@",attribute, searchString];
    }
    NSError *error;
    NSFetchedResultsController *fetchedController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:[HJCoreDataManager sharedInstanceManager].objectContext sectionNameKeyPath:nil cacheName:nil];
    //執(zhí)行獲取操作
    if ([fetchedController performFetch:&error]) {
        //獲取數(shù)據(jù)
        return [fetchedController fetchedObjects];
    } else {
        return @[];
    }
}

5.4 數(shù)據(jù)操作例子

封裝了之后,調(diào)用數(shù)據(jù)的增刪改查就會更加的方便,程序啟動后,在"- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions"方法中去調(diào)用 "[HJCoreDataManager sharedInstanceManager];"初始化對象,然后根據(jù)需求調(diào)用所封裝的操作數(shù)據(jù)方法進(jìn)行讀寫操作。增刪改查示例:

#pragma mark - Core Data數(shù)據(jù)操作


/**
 增加數(shù)據(jù)

 @param sender sender description
 */
- (IBAction)coreDataAddInfoButtonDidClicked:(UIButton *)sender {

    HJCarModel *carModel = (HJCarModel *)[HJCoreDataManager getTableWithEntityName:NSStringFromClass([HJCarModel class])];
    carModel.carName = @"AE99";
    carModel.userName = @"一個大孩子";
    carModel.userID = 11;
    [HJCoreDataManager save];
}

/**
 刪除數(shù)據(jù)

 @param sender sender description
 */
- (IBAction)coreDataDeleteInfoButtonDidClicked:(UIButton *)sender {
    BOOL isSuccess = [HJCoreDataManager deleteByEntityName:NSStringFromClass([HJCarModel class])
                                   withMaching:@"AE99"
                                 withAttribute:@"carName"];
    NSLog(@"%d",isSuccess);
}


/**
 修改數(shù)據(jù)

 @param sender sender description
 */
- (IBAction)coreDataUpdateInfoButtonDidClicked:(UIButton *)sender {
    //修改已經(jīng)保存到數(shù)據(jù)庫中的數(shù)據(jù),在修改之前我們應(yīng)該獲取要修改的數(shù)據(jù),修改之后對數(shù)據(jù)進(jìn)行再次保存
    NSArray *array = [HJCoreDataManager selectByEntityName:NSStringFromClass([HJCarModel class])
                                     withMaching:nil
                                   withAttribute:nil
                                       sortingBy:@"userID"
                                     isAscending:YES];
    HJCarModel *model = array.firstObject;
    model.userName = @"愛聽話的孩子";
    BOOL isSuccess = [HJCoreDataManager updateManagedObject:model];
    NSLog(@"%d",isSuccess);
}

/**
 查詢數(shù)據(jù)

 @param sender sender description
 */
- (IBAction)coreDataSelectInfoButtonDidClicked:(UIButton *)sender {
    NSArray *array = [HJCoreDataManager selectByEntityName:NSStringFromClass([HJCarModel class])
                                     withMaching:nil
                                   withAttribute:nil
                                       sortingBy:nil
                                     isAscending:YES];
    if (array.count > 0) {
        for (HJCarModel *model in array) {
            NSLog(@"%@---%@---%lld", model.userName,model.carName,model.userID);
        }
    }
    
}

6 SQLite3

SQLite是輕量級的數(shù)據(jù)庫,占用資源很少,最初是用于嵌入式的系統(tǒng),在iOS中使用SQLite,需要加入"libsqlite3.tbd"依賴庫并導(dǎo)入頭文件。不應(yīng)該頻繁的打開關(guān)閉數(shù)據(jù)庫,有可能會影響性能, 應(yīng)在啟動程序時打開數(shù)據(jù)庫,在退出程序是關(guān)閉數(shù)據(jù)庫

6.1 封裝SQLite3

創(chuàng)建名為HJSQLiteDBManager的類繼承自NSObject,導(dǎo)入sqlite3頭文件,.h提供操作數(shù)據(jù)庫方法,.m是方法的實(shí)現(xiàn)

#import <Foundation/Foundation.h>


NS_ASSUME_NONNULL_BEGIN

@interface HJSQLiteDBManager : NSObject

/**
 單利創(chuàng)建Manager

 @return return value description
 */
+ (instancetype)sharedInstance;

/**
 打開數(shù)據(jù)庫
 */
+ (void)openDB;

/**
 關(guān)閉數(shù)據(jù)庫
 */
+ (void)closeDB;

/**
 刪除數(shù)據(jù)庫

 @return BOOL
 */
+ (BOOL)deleteSQliteDB;

/**
 創(chuàng)建表

 @param sql sql語句
 @return BOOL
 */
+ (BOOL)createTableWithSql:(NSString *)sql;

/**
 增刪改操作
 
 @param sql sql語句
 @return BOOL
 */
+ (BOOL)operationRecordWithSql:(NSString *)sql;

/**
 查詢記錄

 @param sql sql語句
 @return NSArray
 */
+ (NSArray *)selectRecordWithSql:(NSString *)sql;
@end

NS_ASSUME_NONNULL_END

//  首先在build phases中去導(dǎo)入libsqlite3.tbd
//
//  不應(yīng)該頻繁的打開關(guān)閉數(shù)據(jù)庫,有可能會影響性能
//  在啟動程序時打開數(shù)據(jù)庫
//  在退出程序是關(guān)閉數(shù)據(jù)庫
//  sqlite3_exec() 就是把提到的三個函數(shù)結(jié)合在了一起:sqlite3_step(), sqlite3_perpare(), sqlite3_finalize()
#import "HJSQLiteDBManager.h"
#import <sqlite3.h>

@interface HJSQLiteDBManager ()

@property (nonatomic, assign) sqlite3 *db;

@end

@implementation HJSQLiteDBManager

#pragma mark - 創(chuàng)建對象
+ (instancetype)sharedInstance {
    static HJSQLiteDBManager *manager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        manager = [[HJSQLiteDBManager alloc] init];
    });
    return manager;
}
- (instancetype)init {
    if (self = [super init]) {

    }
    return self;
}
#pragma mark - 打開數(shù)據(jù)庫
+ (void)openDB {
    sqlite3 *sql = [HJSQLiteDBManager sharedInstance].db;
    sqlite3_open([[HJSQLiteDBManager getDBpath] UTF8String], &sql);
    [HJSQLiteDBManager sharedInstance].db = sql;
}

+ (NSString *)getDBpath {
    NSString *documentStr = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
    NSString *sqlDBFolder = [documentStr stringByAppendingPathComponent:@"SQLiteDB"];
    if (![[NSFileManager defaultManager] fileExistsAtPath:sqlDBFolder]) {
        [[NSFileManager defaultManager] createDirectoryAtPath:sqlDBFolder withIntermediateDirectories:YES attributes:nil error:nil];
    }
    return [sqlDBFolder stringByAppendingPathComponent:@"dataDemo.sqlite"];
}

#pragma mark - 關(guān)閉數(shù)據(jù)庫
+ (void)closeDB {
    sqlite3_close([HJSQLiteDBManager sharedInstance].db);
}

#pragma mark - 刪除數(shù)據(jù)庫
+ (BOOL)deleteSQliteDB {
    NSString *documentStr = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
    NSString *sqlDBFolder = [documentStr stringByAppendingPathComponent:@"SQLiteDB"];
    NSError *error;
    if ([[NSFileManager defaultManager] fileExistsAtPath:sqlDBFolder]) {
        [[NSFileManager defaultManager] removeItemAtPath:sqlDBFolder error:&error];
    }
    if (error) {
        return NO;
    }
    return YES;
}

#pragma mark - 創(chuàng)建表
+ (BOOL)createTableWithSql:(NSString *)sql {
    sqlite3 *sqlite = [[HJSQLiteDBManager sharedInstance] db];
    char *errorMsg = "";
    if (sqlite3_exec(sqlite, [sql UTF8String], nil, nil, &errorMsg) == SQLITE_OK) {
        return YES;
    } else {
        NSLog(@"創(chuàng)建表失敗");
        return NO;
    }
}

#pragma mark - 查詢
+ (NSArray *)selectRecordWithSql:(NSString *)sql {
    sqlite3_stmt *stmt;
    if (sqlite3_prepare_v2([HJSQLiteDBManager sharedInstance].db, [sql UTF8String], -1, &stmt, nil) == SQLITE_OK) {
        NSMutableArray *array = [NSMutableArray array];
        //執(zhí)行sql語句
        while (sqlite3_step(stmt) == SQLITE_ROW) {
            //獲取列數(shù)
            int columnCount = sqlite3_column_count(stmt);
            //獲取每一列的值與列明放入字典數(shù)組中
            NSMutableDictionary *dict = [NSMutableDictionary dictionary];
            for (int i = 0; i < columnCount; i++) {
                //獲取值
                char *value = (char *)sqlite3_column_text(stmt, i);
                NSString *valueStr;
                if (value != NULL) {
                    valueStr = [NSString stringWithUTF8String:value];
                }
                //獲取key
                char *key = (char *)sqlite3_column_name(stmt, i);
                NSString *keyStr = [NSString stringWithUTF8String:key];
                dict[keyStr] = valueStr;
            }
            [array addObject:dict];
        }
        sqlite3_finalize(stmt);
        return array;
    }
    return @[];
}


/**
 增刪改操作記錄

 @param sql sql語句
 @return BOOL
 */
+ (BOOL)operationRecordWithSql:(NSString *)sql {
    //對sql執(zhí)行預(yù)編譯
    sqlite3_stmt *stmt;
    //sqlite3_prepare_v2是執(zhí)行查詢的方法
    if (sqlite3_prepare_v2([HJSQLiteDBManager sharedInstance].db, [sql UTF8String], -1, &stmt, nil) == SQLITE_OK) {
        //執(zhí)行sql語句
        if (sqlite3_step(stmt) == SQLITE_DONE) {
            sqlite3_finalize(stmt);
            return YES;
        }
    }
    return NO;
}

@end

6.2 數(shù)據(jù)操作示例

封裝之后,在"- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions"方法中去調(diào)用 "[HJSQLiteDBManager openDB];"打開數(shù)據(jù)庫,然后根據(jù)需求調(diào)用所封裝的操作數(shù)據(jù)方法進(jìn)行讀寫操作。增刪改查示例:

#pragma mark - SQLite3數(shù)據(jù)操作


/**
 添加數(shù)據(jù)

 @param sender sender description
 */
- (IBAction)sqlAddInfoButtonDidClicked:(UIButton *)sender {
    NSString *sqlStr = [NSString stringWithFormat:@"CREATE TABLE IF NOT EXISTS %@(userId INT PRIMARY KEY NOT NULL, name CHAR(20) NOT NULL, age INT)",NSStringFromClass([HJPersonModel class])];
    [HJSQLiteDBManager createTableWithSql:sqlStr];
    NSString *insetSql = [NSString stringWithFormat:@"INSERT INTO %@ (userId, name) VALUES (2, '小金人')", NSStringFromClass([HJPersonModel class])];
    [HJSQLiteDBManager operationRecordWithSql:insetSql];
}
/**
 刪除數(shù)據(jù)
 
 @param sender sender description
 */
- (IBAction)sqlDeleteInfoButtonDidClicked:(UIButton *)sender {
    NSString *deleteSql = [NSString stringWithFormat:@"DELETE FROM %@ WHERE userId = 2", NSStringFromClass([HJPersonModel class])];
    [HJSQLiteDBManager operationRecordWithSql:deleteSql];
}
/**
 修改數(shù)據(jù)
 
 @param sender sender description
 */
- (IBAction)sqlUpdateInfoButtonDidClicked:(UIButton *)sender {
    NSString *updateSql = [NSString stringWithFormat:@"UPDATE %@ SET NAME = '小可愛', AGE = 90 WHERE userId = 2", NSStringFromClass([HJPersonModel class])];
    [HJSQLiteDBManager operationRecordWithSql:updateSql];
}
/**
 查詢數(shù)據(jù)
 
 @param sender sender description
 */
- (IBAction)sqlSelectInfoButtonDidClicked:(UIButton *)sender {
    NSString *selectSql = [NSString stringWithFormat:@"SELECT * FROM %@", NSStringFromClass([HJPersonModel class])];
    NSArray *array = [HJSQLiteDBManager selectRecordWithSql:selectSql];
    NSLog(@"%@",array);
}

7 FMDB

FMDB以O(shè)C的方式封裝了SQLite的C語言API,減去了冗余的C語言代碼,使得API更具有OC的風(fēng)格,更加的面向?qū)ο螅鄬τ贑ore Data框架更加的輕量級,F(xiàn)MDB還提供了多線程安全的數(shù)據(jù)庫操作方法,在FMDB中有三個重要的概念:

FMDatabase:一個FMDatabase就代表一個SQLite數(shù)據(jù)庫,執(zhí)行sql語句

FMResultSet:執(zhí)行查詢后的結(jié)果集

FMDatabaseQueue:用于在多線程中執(zhí)行多個查詢或更新,安全的

7.1 封裝FMDB

FMDB的導(dǎo)入可以直接使用CocoaPods,導(dǎo)入后需要對其進(jìn)行封裝以擁有更加友好的API。創(chuàng)建HJFMDBManager類繼承自NSObject,導(dǎo)入FMDatabase和FMDatabaseQueue類,.h文件中提供操作數(shù)據(jù)庫方法,.m提供方法的實(shí)現(xiàn)

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface HJFMDBManager : NSObject

/**
 單利manager

 @return return value description
 */
+ (instancetype)sharedInstance;

/**
 打開數(shù)據(jù)庫

 @return BOOL
 */
+ (BOOL)openDBForQueue;

/**
 關(guān)閉數(shù)據(jù)庫
 */
+ (void)closeDBForQueue;



/**
 刪除數(shù)據(jù)庫

 @return BOOL
 */
+ (BOOL)deleteFMDB;


/**
 執(zhí)行增刪改建表

 @param sql sql語句
 */
+ (BOOL)executeWithSql:(NSString *)sql;

/**
 異步,執(zhí)行增刪改建表

 @param sql sql語句
 @param successBlock block
 */
+ (void)executeAsynWithSql:(NSString *)sql
                 isSuccess:(void(^)(BOOL isSuccess))successBlock;

/**
 執(zhí)行查詢數(shù)據(jù)
 
 @param sql sql語句
 */
+ (NSArray *)executeQueryWithSql:(NSString *)sql;

/**
 異步,執(zhí)行查詢數(shù)據(jù)

 @param sql sql語句
 @param successBlock block
 */
+ (void)executeAsynQueryWithSql:(NSString *)sql
                      isSuccess:(void(^)(NSArray *resultArray))successBlock;

@end

NS_ASSUME_NONNULL_END
#import "HJFMDBManager.h"
#import "fmdb/FMDatabase.h"
#import "fmdb/FMDatabaseQueue.h"

@interface HJFMDBManager ()

@property (nonatomic, strong) FMDatabase *db;
@property (nonatomic, strong) FMDatabaseQueue *dbQueue;

@end

@implementation HJFMDBManager

#pragma mark 創(chuàng)建manager
+ (instancetype)sharedInstance {
    static HJFMDBManager *manager;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        manager = [[HJFMDBManager alloc] init];
    });
    return manager;
}

#pragma mark - 打開數(shù)據(jù)庫
+ (BOOL)openDBForQueue {
    //獲取路徑
    NSString *documentPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
    //FMDB文件夾
    NSString *fmdbFolder = [documentPath stringByAppendingPathComponent:@"FMDB"];
    //判斷文件夾是否存在
    if (![[NSFileManager defaultManager] fileExistsAtPath:fmdbFolder]) {
        [[NSFileManager defaultManager] createDirectoryAtPath:fmdbFolder withIntermediateDirectories:YES attributes:nil error:nil];
    }
    //數(shù)據(jù)庫路徑
    NSString *dbPath = [fmdbFolder stringByAppendingPathComponent:@"fmdb.db"];
    FMDatabase *db = [FMDatabase databaseWithPath:dbPath];
    if ([db open]) {
        [HJFMDBManager sharedInstance].db = db;
        [HJFMDBManager sharedInstance].dbQueue = [FMDatabaseQueue databaseQueueWithPath:dbPath];
        return YES;
    }
    return NO;
}

#pragma mark - 關(guān)閉數(shù)據(jù)庫
+ (void)closeDBForQueue {
    [[[HJFMDBManager sharedInstance] dbQueue] close];
}

#pragma mark - 刪除數(shù)據(jù)庫
+ (BOOL)deleteFMDB {
    NSString *documentStr = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
    NSString *fmdbFolder = [documentStr stringByAppendingPathComponent:@"FMDB"];
    NSError *error;
    if ([[NSFileManager defaultManager] fileExistsAtPath:fmdbFolder]) {
        [[NSFileManager defaultManager] removeItemAtPath:fmdbFolder error:&error];
    }
    if (error) {
        return NO;
    }
    return YES;
}

#pragma mark - 執(zhí)行增刪改建表
+ (BOOL)executeWithSql:(NSString *)sql {
    return [[HJFMDBManager sharedInstance].db executeUpdate:sql];
}

#pragma mark - 異步,執(zhí)行增刪改建表
+ (void)executeAsynWithSql:(NSString *)sql
                     isSuccess:(void(^)(BOOL isSuccess))successBlock {
    [[[HJFMDBManager sharedInstance] dbQueue] inDatabase:^(FMDatabase * _Nonnull db) {
        BOOL isSuccess = [db executeUpdate:sql];
        if (successBlock) {
            successBlock(isSuccess);
        }
    }];
}

#pragma mark - 執(zhí)行查詢數(shù)據(jù)
+ (NSArray *)executeQueryWithSql:(NSString *)sql {
    FMResultSet *resultSet = [[HJFMDBManager sharedInstance].db executeQuery:sql];
    NSMutableArray *array = [NSMutableArray array];
    while (resultSet.next) {
        [array addObject:resultSet.resultDictionary];
    }
    return array;
}

#pragma mark - 異步,執(zhí)行查詢數(shù)據(jù)
+ (void)executeAsynQueryWithSql:(NSString *)sql
                      isSuccess:(void(^)(NSArray *resultArray))successBlock {
    [[[HJFMDBManager sharedInstance] dbQueue] inDatabase:^(FMDatabase * _Nonnull db) {
        FMResultSet *resultSet = [db executeQuery:sql];
        NSMutableArray *array = [NSMutableArray array];
        while (resultSet.next) {
            [array addObject:resultSet.resultDictionary];
        }
        if (successBlock) {
            successBlock(array);
        }
    }];
}

7.2 數(shù)據(jù)操作示例

在"- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions"方法中去調(diào)用 "[HJFMDBManager openDBForQueue];"打開數(shù)據(jù)庫,然后根據(jù)需求調(diào)用所封裝的操作數(shù)據(jù)方法進(jìn)行讀寫操作。增刪改查示例:

#pragma mark - FMDB數(shù)據(jù)庫操作

/**
 增加記錄

 @param sender sender description
 */
- (IBAction)fmdbInsertButtonDidClicked:(UIButton *)sender {
    NSString *creatStr =[NSString stringWithFormat:@"CREATE TABLE IF NOT EXISTS %@(userId INT PRIMARY KEY NOT NULL, name CHAR(20) NOT NULL, age INT)", NSStringFromClass([HJPersonModel class])];
    [HJFMDBManager executeAsynWithSql:creatStr isSuccess:^(BOOL isSuccess) {
        if (isSuccess) {
            NSLog(@"創(chuàng)建表成功");
        }
    }];
    NSString *insertStr = [NSString stringWithFormat:@"INSERT INTO %@ VALUES (2, '小金人', 25)", NSStringFromClass([HJPersonModel class])];
    [HJFMDBManager executeAsynWithSql:insertStr isSuccess:^(BOOL isSuccess) {
        if (isSuccess) {
            NSLog(@"插入數(shù)據(jù)成功");
        }
    }];
}
/**
 刪除記錄
 
 @param sender sender description
 */
- (IBAction)fmdbDeleteButtonDidClicked:(UIButton *)sender {
    NSString *insertStr = [NSString stringWithFormat:@"DELETE FROM %@ WHERE USERID = 2", NSStringFromClass([HJPersonModel class])];
    [HJFMDBManager executeAsynWithSql:insertStr isSuccess:^(BOOL isSuccess) {
        if (isSuccess) {
            NSLog(@"刪除數(shù)據(jù)成功");
        }
    }];
}
/**
 修改記錄
 
 @param sender sender description
 */
- (IBAction)fmdbUpdateButtonDidClicked:(UIButton *)sender {
    NSString *insertStr = [NSString stringWithFormat:@"UPDATE %@ SET userid = 11, NAME = '小可愛', AGE = 90 WHERE userId = 2", NSStringFromClass([HJPersonModel class])];
    [HJFMDBManager executeAsynWithSql:insertStr isSuccess:^(BOOL isSuccess) {
        if (isSuccess) {
            NSLog(@"修改數(shù)據(jù)成功");
        }
    }];
}
/**
 查詢記錄
 
 @param sender sender description
 */
- (IBAction)fmdbSelectButtonDidClicked:(UIButton *)sender {
    NSString *selectSql = [NSString stringWithFormat:@"SELECT * FROM %@", NSStringFromClass([HJPersonModel class])];
    [HJFMDBManager executeAsynQueryWithSql:selectSql
                                 isSuccess:^(NSArray * _Nonnull resultArray) {
                                     NSLog(@"%@",resultArray);
                                 }];
}

8 Realm

Realm是一款可以用于iOS(同樣適用于Swift&Objective-C)和Android的跨平臺移動數(shù)據(jù)庫。Realm極大地減小了學(xué)習(xí)成本,沒有Core Data與SQLite冗余、復(fù)雜的知識和代碼,更加的面向?qū)ο?。Realm還提供了一個輕量級的數(shù)據(jù)庫查看工具"Realm Browser",可以非常簡便的查看數(shù)據(jù)庫當(dāng)中的內(nèi)容

8.1 Realm的安裝

Realm的安裝方法有很多種,使用 Dynamic Framework、CocoaPods等方法都行,這里僅介紹一種

1、下載最新的Realm版本,并解壓,注意選擇下載適用的語言,OC應(yīng)選擇下載 "realm-objc"版本

2、選擇Xcode 工程的”General”設(shè)置項(xiàng),從文件夾ios/dynamic/、osx/、tvos/或者watchos/中將’Realm.framework’拖曳到”Embedded Binaries”選項(xiàng)中。勾選上Copy items if needed,點(diǎn)擊Finish按鈕

3、選擇 Target 的”Build Settings”中,在”Framework Search Paths”中添加Realm.framework的上級目錄;

4、在 iOS、watchOS 或者 tvOS 項(xiàng)目中使用 了Realm,在”Build Phases”中,創(chuàng)建一個新的”Run Script Phase”,將腳本復(fù)制到文本框中用來繞過APP商店提交的bug,這在打包通用設(shè)備的二進(jìn)制發(fā)布版本時是必須的

bash "${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/Realm.framework/strip-frameworks.sh"

8.2 Realm的重要概念

RLMRealm:RLMRealm是框架的核心,如同Core Data的托管對象上下文

RLMObject:數(shù)據(jù)模型,創(chuàng)建自定義的數(shù)據(jù)模型需要繼承自RLMObject

Relationships:在數(shù)據(jù)模型中聲明一個RLMObject類型的屬性,就可以創(chuàng)建一個對象關(guān)系

Write Transactions:寫操作事物,數(shù)據(jù)庫中的創(chuàng)建、刪除對象等,都應(yīng)該在事物中完成

Queries:查詢數(shù)據(jù)庫中的信息,可以使用斷言、復(fù)合查詢等進(jìn)行數(shù)據(jù)查詢

RLMResults:查詢結(jié)果集,其中包含一系列的RLMObject對象,RLMResults和NSArray類似,我們可以用下標(biāo)語法來對其進(jìn)行訪問

8.3 Realm數(shù)據(jù)庫的使用

在實(shí)際開發(fā)中,通常都需要對Core Data、SQLite3、FMDB分裝一個單例類。
但是Realm數(shù)據(jù)庫通過[RLMRealm defaultRealm]就可以獲取到當(dāng)前realm對象的一個實(shí)例,它本身就是一個單利,所以我們每次在子線程里面不要再去讀取我們自己封裝持有的realm實(shí)例了,直接調(diào)用系統(tǒng)的這個方法即可。需要注意的是同一個Realm對象是不支持跨線程操作realm數(shù)據(jù)庫的,在Realm中每個線程都擁有Realm的一個快照,可以同時有任意數(shù)目的線程訪問同一個 Realm 文件,線程之間不會產(chǎn)生影響

8.3.1 創(chuàng)建數(shù)據(jù)庫

在"- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions"方法中創(chuàng)建數(shù)據(jù)庫

    NSString *documentPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
    NSString *dbFolder = [documentPath stringByAppendingPathComponent:@"RealmDB"];
    if (![[NSFileManager defaultManager] fileExistsAtPath:dbFolder]) {
        [[NSFileManager defaultManager] createDirectoryAtPath:dbFolder withIntermediateDirectories:YES attributes:nil error:nil];
    }
    NSString *filePath = [dbFolder stringByAppendingPathComponent:@"db.realm"];
    RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
    //路徑
    config.fileURL = [NSURL URLWithString:filePath];
    //是否只讀
    config.readOnly = NO;
    //當(dāng)前版本
    config.schemaVersion = 4;
    [RLMRealmConfiguration setDefaultConfiguration:config];

8.3.2 創(chuàng)建數(shù)據(jù)模型

Realm中創(chuàng)建自定義的數(shù)據(jù)模型需要繼承自RLMObject,所以可以創(chuàng)建一個名為HJRealmBaseModel自定義類先繼承自RLMObject

#import <UIKit/UIKit.h>
#import <Realm/Realm.h>

NS_ASSUME_NONNULL_BEGIN

@interface HJRealmBaseModel : RLMObject

@end

NS_ASSUME_NONNULL_END
#import "HJRealmBaseModel.h"

@implementation HJRealmBaseModel

@end

創(chuàng)建一個HJRealmPersonModel類和HJRealmCarModel類繼承自HJRealmBaseModel,
HJRealmPersonModel對于HJRealmCarModel的關(guān)系為一對多,由于Realm數(shù)據(jù)庫不支持集合類型,比如說NSArray,NSMutableArray,NSDictionary,NSMutableDictionary,NSSet,NSMutableSet,所以集合類型的屬性是不能直接存儲到數(shù)據(jù)庫的。當(dāng)然Realm里面是有集合的,就是RLMArray,這里面裝的都是RLMObject。
解決這個問題,如果是model,就先自己接收一下,然后將數(shù)據(jù)轉(zhuǎn)換成RLMObject的model,在存儲到RLMArray里,然后使用"ignoredProperties"方法忽略掉不能存儲的屬性

HJRealmCarModel.h

#import "HJRealmBaseModel.h"

NS_ASSUME_NONNULL_BEGIN

@interface HJRealmCarModel : HJRealmBaseModel

@property (nonatomic, assign) NSInteger carId;
@property (nonatomic, copy) NSString *carName;

@end
//RLM_ARRAY_TYPE宏創(chuàng)建了一個協(xié)議,從而允許 RLMArray語法的使用
RLM_ARRAY_TYPE(HJRealmCarModel)

NS_ASSUME_NONNULL_END

HJRealmCarModel.m


#import "HJRealmCarModel.h"

@implementation HJRealmCarModel

/**
 主鍵

 @return return value description
 */
+ (NSString *)primaryKey {
    return @"carId";
}


/**
 屬性值不能為空

 @return return value description
 */
+ (NSArray<NSString *> *)requiredProperties {
    return @[@"carId"];
}

/**
 忽略屬性

 @return return value description
 */
+ (NSArray *)ignoredProperties {
    return @[];
}

@end

HJRealmPersonModel.h

#import "HJRealmBaseModel.h"
#import "HJRealmCarModel.h"

NS_ASSUME_NONNULL_BEGIN

@interface HJRealmPersonModel : HJRealmBaseModel

@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger userId;
@property (nonatomic, assign) NSInteger age;
@property (nonatomic, strong) NSString *address;
@property (nonatomic, strong) RLMArray<HJRealmCarModel *><HJRealmCarModel> *cars;

@end
NS_ASSUME_NONNULL_END

HJRealmPersonModel.m

#import "HJRealmPersonModel.h"

@implementation HJRealmPersonModel

+ (NSString *)primaryKey {
    return @"userId";
}

+ (NSArray<NSString *> *)requiredProperties {
    return @[@"userId"];
}

+ (NSArray *)ignoredProperties {
    return @[];
}

@end

8.3.3 數(shù)據(jù)的增刪改查示例

Realm數(shù)據(jù)庫的操作都應(yīng)該在事物當(dāng)中進(jìn)行

#pragma mark - Realm數(shù)據(jù)操作
/**
 增加記錄
 
 @param sender sender description
 */
- (IBAction)realmInsertButtonDidClicked:(UIButton *)sender {
    RLMArray<HJRealmCarModel *><HJRealmCarModel> *car;
    HJRealmCarModel *carModel = [HJRealmCarModel new];
    carModel.carId = 111111;
    [car addObject:carModel];
    HJRealmPersonModel *model = [HJRealmPersonModel new];
    model.userId = 1;
    model.cars = car;
    [[RLMRealm defaultRealm] transactionWithBlock:^{
        [[RLMRealm defaultRealm] addOrUpdateObject:model];
    }];
}
/**
 刪除記錄
 
 @param sender sender description
 */
- (IBAction)realmDeleteButtonDidClicked:(UIButton *)sender {
    [[RLMRealm defaultRealm] transactionWithBlock:^{
        RLMResults *results = [HJRealmPersonModel objectsWhere:@"userId = 1"];
        NSLog(@"%@",results);
        [[RLMRealm defaultRealm] deleteObjects:results];
    }];
    
}
/**
 修改記錄
 
 @param sender sender description
 */
- (IBAction)realmUpdateButtonDidClicked:(UIButton *)sender {
    HJRealmPersonModel *model = [HJRealmPersonModel new];
    model.userId = 1;
    model.name = @"小星星";
    [[RLMRealm defaultRealm] transactionWithBlock:^{
        [[RLMRealm defaultRealm] addOrUpdateObject:model];
    }];
}
/**
 查詢記錄
 
 @param sender sender description
 */
- (IBAction)realmSelectButtonDidClicked:(UIButton *)sender {
    [[RLMRealm defaultRealm] transactionWithBlock:^{
        RLMResults *results = [HJRealmPersonModel objectsWhere:@"userId = 1"];
        NSLog(@"%@",results.firstObject);
    }];
}

總結(jié)
經(jīng)過上面的分析
plist文件主要用于不經(jīng)常修改、數(shù)據(jù)量小的數(shù)據(jù)
偏好設(shè)置主要用于檢查版本是否更新、是否啟動引導(dǎo)頁、自動登錄、版本號等程序設(shè)置歸
檔解歸檔主要是歸檔之后的文件時加密的,用于一些數(shù)據(jù)量小的數(shù)據(jù),數(shù)據(jù)庫操作比較笨拙
沙盒路徑可以提高程序的體驗(yàn)度,為用戶節(jié)約數(shù)據(jù)流量,主要在用戶閱讀書籍、聽音樂、看視頻等
Core Data提供了對象關(guān)系的映射功能,使得能夠?qū)C對象轉(zhuǎn)換成數(shù)據(jù),將數(shù)據(jù)庫中的數(shù)據(jù)還原成OC對象,在轉(zhuǎn)換的過程中不需要編寫任何的SQL語句
SQLite3是輕量級的數(shù)據(jù)庫,占用資源很少,需要大量的SQL語句
FMDB是對SQLite3的進(jìn)一步封裝,減去了C語言風(fēng)格,更加的面向?qū)ο?,同樣需要大量的SQL語句
Realm本質(zhì)上也是一個嵌入式數(shù)據(jù)庫,更加的輕量級,支持跨平臺,沒有Core Data與SQLite冗余、復(fù)雜的知識和代碼,更加的面向?qū)ο螅瑢W(xué)習(xí)成本更小

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容