文章來源:愛聽音樂的狗博客
數(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ù),如圖所示:


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方式、類并且輸入名稱

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í)體中添加屬性及類別

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

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í)成本更小