目錄
1.plist(屬性列表)文件
2.Preference\NSUserDefaults(偏好設(shè)置)
3.NSKetedArchiver(歸檔 - 解歸檔)
4.SQLite 與 FMDB
5.CoreData
6.鑰匙串 Keychain
7.云服務(wù) iCloud
8.YYCache 緩存
9.bbx 司機(jī)端舉例

數(shù)據(jù)存儲緩存基礎(chǔ)知識
數(shù)據(jù)存儲也叫數(shù)據(jù)緩存也叫數(shù)據(jù)持久化。所謂的持久化就是將數(shù)據(jù)保存到硬盤中,使得在應(yīng)用程序或機(jī)器重啟后可以繼續(xù)訪問之前保存的數(shù)據(jù)。在 iOS 開發(fā)中有很多數(shù)據(jù)持久化的方案,比如 plist、偏好設(shè)置、歸檔 - 解歸檔、SQLite、FMDB、CoreData、
Keychain 等。內(nèi)存緩存:也叫網(wǎng)絡(luò)緩存;是 App 在運(yùn)行的時候用到的,你把他想成單例的存放。即指當(dāng)前程序運(yùn)行空間,內(nèi)存緩存速度快容量小,它是供 cpu 直接讀取,比如我們打開一個程序,他是運(yùn)行在內(nèi)存中的,關(guān)閉程序后內(nèi)存又會釋放。內(nèi)存緩存的數(shù)據(jù)只保留在APP啟動時,比如保存一些從服務(wù)端獲取到的數(shù)據(jù),來緩解服務(wù)器的壓力,并且節(jié)約了用戶流量和時間,提高了用戶使用體驗(yàn)。iOS 內(nèi)存分為5個區(qū):棧區(qū),堆區(qū),全局區(qū),常量區(qū),代碼區(qū)。
硬盤緩存:也叫本地緩存,還叫磁盤緩存;就是存在沙盒。磁盤存儲又可以分為文件系統(tǒng)存儲和數(shù)據(jù)庫系統(tǒng)存儲。
相關(guān)鏈接:https://juejin.cn/post/6844903593913352206


1.內(nèi)存分區(qū)

2.內(nèi)存緩存和磁盤緩存的區(qū)別
緩存分為內(nèi)存緩存和磁盤緩存兩種。
1.內(nèi)存是指當(dāng)前程序的運(yùn)行空間,緩存速度快容量小,是臨時存儲文件用的,供 CPU 直接讀取,比如說打開一個程序,他是在內(nèi)存中存儲,關(guān)閉程序后內(nèi)存就又回到原來的空閑空間。內(nèi)存緩存適合存儲一些 app 高頻次使用,并且所占空間不大的文件,比如 NSURLConnection 默認(rèn)會緩存資源在內(nèi)存。
2.磁盤是程序的存儲空間,緩存容量大、讀取速度慢、可持久化。與內(nèi)存不同的是磁盤是永久存儲東西的,只要里面存放東西,不管運(yùn)行不運(yùn)行 ,他都占用空間,磁盤緩存是存在 Library/Caches。磁盤存儲可以存儲一些需要持久化的文件,信息等,簡單的講就是 app 被殺死以后,文件仍然在。


3.沙盒詳解
沙盒機(jī)制:iOS 為不同數(shù)據(jù)管理對存儲路徑做了規(guī)范,即在 iOS 中每個 APP 都擁有自己的沙盒,APP 只能訪問對應(yīng)沙盒中存儲的數(shù)據(jù),iOS 是不允許跨越沙盒去訪問數(shù)據(jù)的,所有的數(shù)據(jù)都是保存在該沙盒的三個子目錄下:Document、Library(Library/Caches,Library/Preference)、temp。
說明:
1.Bundle 和沙盒(sandbox)之間的區(qū)別:Bundle 是應(yīng)用程序在手機(jī)中的安裝路徑。沙盒(sandbox)是專門來存儲當(dāng)前APP自己的數(shù)據(jù)的路徑。
2.Application(應(yīng)用程序包):包含了所有的資源文件和和可執(zhí)行文件,上架前經(jīng)過數(shù)字簽名,上架后不可修改。
3.沙盒根目錄獲?。篘SString *home = NSHomeDirectory();
相關(guān)鏈接:http://www.itdecent.cn/p/34cda6a121db

4.緩存做什么?
我們使用場景比如:離線加載,預(yù)加載,本地通訊錄...等,對非網(wǎng)絡(luò)數(shù)據(jù),使用本地數(shù)據(jù)管理的一種,具體使用場景有很多。
5.NSCache
NSCache 是蘋果提供的一套緩存機(jī)制,用法和 NSMutableDictionary 類似,在
AFNetworking,SDWebImage,Kingfisher 中都有用到。它相當(dāng)于字典的安全版本,不會出現(xiàn)多線程的問題,線程安全的。說明:
1.當(dāng)內(nèi)存不足時 NSCache 會自動釋放內(nèi)存,NSCache 設(shè)置緩存對象數(shù)量和占用的內(nèi)存大小,當(dāng)緩存超出了設(shè)置會自動釋放內(nèi)存。
2.NSCache 是 Key-Value 數(shù)據(jù)結(jié)構(gòu),其中 key 是強(qiáng)引用,不實(shí)現(xiàn) NSCoping 協(xié)議,作為 key 的對象不會被拷貝。


- 舉例:
@interface NSCacheVC ()<NSCacheDelegate>
@property (nonatomic, strong) NSCache *myCache;
@end
@implementation NSCacheVC
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.view.backgroundColor = [UIColor purpleColor];
self.myCache = [[NSCache alloc]init];
self.myCache.delegate = self;
for (int i = 0; i<10; i++) {
[self.myCache setObject:[NSString stringWithFormat:@"%d", i] forKey:@(i) cost: 1];
}
for (int i = 0; i<10; i++) {
NSLog(@"NSCache取出---%@", [self.myCache objectForKey:@(i)]);
}
/// 清除緩存
[self.myCache removeAllObjects];
/// 設(shè)置緩存限制
self.myCache.totalCostLimit = 5;
NSLog(@"設(shè)置緩存限制后=================");
for (int i = 0; i<10; i++) {
// 設(shè)置成本數(shù)為1
[self.myCache setObject:[NSString stringWithFormat:@"%d", i] forKey:@(i) cost: 1];
}
for (int i = 0; i<10; i++) {
NSLog(@"NSCache取出---%@", [self.myCache objectForKey:@(i)]);
}
/// 清除緩存
[self.myCache removeAllObjects];
NSLog(@"設(shè)置緩存限制后但未設(shè)置成本數(shù)cost=================");
for (int i = 0; i<10; i++) {
[self.myCache setObject:[NSString stringWithFormat:@"%d", i] forKey:@(i)];
}
for (int i = 0; i<10; i++) {
NSLog(@"NSCache取出---%@", [self.myCache objectForKey:@(i)]);
}
/// 清除緩存
[self.myCache removeAllObjects];
}
// 即將回收對象的時候進(jìn)行調(diào)用,實(shí)現(xiàn)代理方法之前要遵守NSCacheDelegate協(xié)議。
- (void)cache:(NSCache *)cache willEvictObject:(id)obj{
NSLog(@"NSCache回收---%@", obj);
}
@end

1.plist(屬性列表)文件
plist:是一種明文的輕量級存儲方式,最常用的格式是 XML 格式,比如新建一個項(xiàng)目時系統(tǒng)會提供一個 info.plist 文件,這種方式的安全性很低,一般使用 plist 文件存儲的數(shù)據(jù)都是不需要加密,存儲少量,簡單的不重要數(shù)據(jù),plist 文件能存儲字典和數(shù)組,不能存儲自定義對象。
說明:
屬性列表是一種 XML 格式的文件,拓展名為 plist 如果是對 NSString、NSDictionary、NSArray、NSData、NSNumber 等類型以及他們的可變類型等,就可以使用 writeToFile:atomically: 方法直接將對象寫到屬性列表文件中存儲,讀取時使用 arrayWithContentsOfFile:(數(shù)組)、dictionaryWithContentsOfFile(字典)等方法。

使用場景:
主要是用于不用加密的數(shù)據(jù)存儲,比如在添加地址中使用的 pickerView 中的 cities.plist 文件。
舉例:
/**
* Plist文件
*/
- (void)setUpPlist {
NSString *path = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
NSString *filePath = [path stringByAppendingPathComponent:@"Person.plist"];
NSDictionary *dict = @{
@"name" : @"William",
@"age" : @18,
@"height" : @1.75f
};
// 將數(shù)據(jù)寫入Plist
[dict writeToFile:filePath atomically:YES];
NSLog(@"%@", filePath);
// 讀取plist中的數(shù)據(jù)
NSDictionary *dicts = [NSDictionary dictionaryWithContentsOfFile:filePath];
NSLog(@"dict = %@",dicts);
NSString *str = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
NSLog(@"str = %@", str);
}

plist 文件其他補(bǔ)充:
plist文件是將某些特定的類,通過XML文件的方式保存在目錄中,可以被序列化的類型只有如下幾種:
NSArray;
NSMutableArray;
NSDictionary;
NSMutableDictionary;
NSData;
NSMutableData;
NSString;
NSMutableString;
NSNumber;
NSDate;
1.獲得文件路徑:
NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
NSString *fileName = [path stringByAppendingPathComponent:@"123.plist"];
2.存儲:
NSArray *array = @[@"123", @"456", @"789"];
[array writeToFile:fileName atomically:YES];
3.讀?。?NSArray *result = [NSArray arrayWithContentsOfFile:fileName];
NSLog(@"%@", result);
4.注意:
只有以上列出的類型才能使用plist文件存儲,不能直接存儲自定義模型對象,如果要自定義則只能將自定義模型對象轉(zhuǎn)換為字典存儲。
存儲時使用 writeToFile: atomically: 方法,其中 atomically 表示是否需要先寫入一個輔助文件,再把輔助文件拷貝到目標(biāo)文件地址。這是更安全的寫入文件方法,一般都寫 YES。
讀取時使用 arrayWithContentsOfFile: 方法。
plist 只能識別字典,數(shù)組。


2.Preference\NSUserDefaults(偏好設(shè)置)
很多 iOS 應(yīng)用都支持偏好設(shè)置來存儲數(shù)據(jù),比如保存用戶名、密碼、字體大小等設(shè)置,iOS 提供了一套標(biāo)準(zhǔn)的解決方案來為應(yīng)用加入偏好設(shè)置功能,每個應(yīng)用都有個 NSUserDefaults 實(shí)例,通過它來存取偏好設(shè)置。注意偏好設(shè)置也不能存儲自定義對象。
說明:
1.偏好設(shè)置是專門用來保存應(yīng)用程序的配置信息的, 一般情況不要在偏好設(shè)置中保存其他數(shù)據(jù)。如果利用系統(tǒng)的偏好設(shè)置來存儲數(shù)據(jù),默認(rèn)就是存儲在 Preferences 文件夾下面的,偏好設(shè)置會將所有的數(shù)據(jù)都保存到同一個文件中。使用偏好設(shè)置對數(shù)據(jù)進(jìn)行保存之后,它保存到系統(tǒng)的時間是不確定的,會在將來某一時間點(diǎn)自動將數(shù)據(jù)保存到 Preferences 文件夾下面。
2.如果需要即刻將數(shù)據(jù)存儲,可以使用 [defaults synchronize],因?yàn)?UserDefaults 設(shè)置數(shù)據(jù)時不是立即寫入,而是根據(jù)時間戳定時地把緩存中的數(shù)據(jù)寫入本地磁盤,所以調(diào)用了 set 方法之后數(shù)據(jù)有可能還沒有寫入磁盤應(yīng)用程序就終止了。出現(xiàn)以上問題,可以通過調(diào)用 synchornize 方法 [defaults synchornize]; 強(qiáng)制寫入。
3.所有的信息都寫在一個文件中,對比簡單的 plist 可以保存和讀取基本的數(shù)據(jù)類型,獲取 NSuserDefaults 保存(讀取)數(shù)據(jù)。
4.對于NSUerdefaults來說 一般可以用來保存用戶的偏好設(shè)置 比如登陸賬號 密碼這些。 除此之外還可以用它來保存圖片, 字符串 , 數(shù)字 和對象。它被保存到項(xiàng)目中的Plists文件里面里。保存圖片 一般用它里面的兩個方法 圖片保存可以用PNG或者JPG對應(yīng)的方法 先轉(zhuǎn)換成NSData 再用NSUerdefaults保存 保存的時候?yàn)榱俗屗R上存下來要用synchronize 。它還可以用來在程序間進(jìn)行反向傳值。
5.注意:偏好設(shè)置是專門用來保存應(yīng)用程序的配置信息的,一般不要在偏好設(shè)置中保存其他數(shù)據(jù);如果沒有調(diào)用synchronize方法,系統(tǒng)會根據(jù)I/O情況不定時刻地保存到文件中,所以如果需要立即寫入文件的就必須調(diào)用synchronize方法;偏好設(shè)置會將所有數(shù)據(jù)保存到同一個文件中。即preference目錄下的一個以此應(yīng)用包名來命名的plist文件。
使用場景:
用來保存應(yīng)用程序設(shè)置和屬性、用戶保存的數(shù)據(jù)。用戶再次打開程序或開機(jī)后這些數(shù)據(jù)仍然存在,比如保存用戶名、密碼。
舉例:





3.NSKetedArchiver(歸檔 - 解歸檔)
歸檔:即序列化,將一個 OC 對象轉(zhuǎn)換成 NSData(二進(jìn)制)的操作就叫做對象的序列化。解歸檔:即反序列化,將本地的二進(jìn)制數(shù)據(jù)轉(zhuǎn)為一個 OC 對象的操作就叫做反序列化。OC 對象需要通過遵守 NSCoding 協(xié)議,并且實(shí)現(xiàn)協(xié)議中的兩個方法,才能支持序列化和反序列化操作。注意歸檔 NSCoding 可以存儲自定義對象。由于決大多數(shù)支持存儲數(shù)據(jù)的 Foundation 和 Cocoa Touch 類都遵循了 NSCoding 協(xié)議,因此對于大多數(shù)類來說歸檔相對而言還是比較容易實(shí)現(xiàn)的。
說明:
1.NSKeyedArchiver 如果對象是 NSString、NSDictionary、NSArray、NSData、NSNumber 等類型,可以直接用 NSKeyedArchiver 進(jìn)行歸檔和恢復(fù)。不是所有的對象都可以直接用這種方法進(jìn)行歸檔,只有遵守了 NSCoding 協(xié)議的對象才可以,并且實(shí)現(xiàn)協(xié)議中的兩個方法。
2.NSCoding 協(xié)議有2個方法:
2.1 encodeWithCoder: 每次歸檔對象時都會調(diào)用這個方法。一般在這個方法里面指定如何歸檔對象中的每個實(shí)例變量,可以使用 encodeObject:forKey: 方法歸檔實(shí)例變量。
2.2 initWithCoder: 每次從文件中恢復(fù)(解碼)對象時都會調(diào)用這個方法。一般在這個方法里面指定如何解碼文件中的數(shù)據(jù)為對象的實(shí)例變量,可以使用 decodeObject:forKey 方法解碼實(shí)例變量。3.如果需要?dú)w檔的類是某個自定義類的子類時,就需要在歸檔和解檔之前先實(shí)現(xiàn)父類的歸檔和解檔方法。即 [super encodeWithCoder:aCoder] 和 [super initWithCoder:aDecoder] 方法,確保繼承的實(shí)例變量也能被編碼,即也能被歸檔,以及也能被解碼,即也能被恢復(fù)。
4.將各種類型的對象存儲到文件中,不僅僅是字符串或者字典,還能實(shí)現(xiàn)對自定義類的對象進(jìn)行歸檔。
5.NSKeyedArchiver 是一種輕量級存儲的持久化方案,數(shù)據(jù)化歸檔時經(jīng)過加密處理的,所以安全性遠(yuǎn)高于 plist,數(shù)據(jù)歸檔可以存儲一些復(fù)雜的對象,數(shù)據(jù)保存前會經(jīng)過二進(jìn)制處理。
6.缺點(diǎn):它的局限是一次性讀取和存儲操作。歸檔的形式來保存數(shù)據(jù)只能一次性歸檔保存以及一次性解壓。所以只能針對小量數(shù)據(jù),如果想改動數(shù)據(jù)的某一小部分,需要解壓整個數(shù)據(jù)或者歸檔整個數(shù)據(jù)。

使用場景:
當(dāng)我們想要存儲一些更加復(fù)雜的自定義數(shù)據(jù)時,如果我們的程序此時不需要執(zhí)行查詢數(shù)據(jù)、數(shù)據(jù)遷移等操作,或者是并非所有的數(shù)據(jù)都有發(fā)雜的關(guān)系圖,這時候數(shù)據(jù)的歸檔方案是最適合的。使用 archive 格式的文件將歸檔的數(shù)據(jù)存儲在沙盒目錄下,這種格式讀取出來的是二進(jìn)制,需要用 NSKeyedUnarchiver 類對該數(shù)據(jù)進(jìn)行解檔。
舉例:



舉例3:
Student.h
#import
#import "Book.h"
@interface Student : NSObject
@property(nonatomic, copy) NSString *name;//姓名
@property(nonatomic, assign) int age;//年齡
@property(nonatomic, strong) Book *book;//課本
@end
Student.m
#import "Student.h"
static NSString * const kStudentNameKey = @"kStudentNameKey";
static NSString * const kStudentAgeKey = @"kStudentAgeKey";
static NSString * const kBookKey = @"kBookKey";
@implementation Student
/**解檔(反序列化)*/
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
if (self == [super init]) {
self.name = [aDecoder decodeObjectForKey:kStudentNameKey];
self.age = [aDecoder decodeIntForKey:kStudentAgeKey];
self.book = [aDecoder decodeObjectForKey:kBookKey];
}
return self;
}
/**歸檔(序列化)*/
- (void)encodeWithCoder:(NSCoder *)aCoder {
// 歸檔姓名(字符串對象)
[aCoder encodeObject:self.name forKey:kStudentNameKey];
// 歸檔年齡(注意:這是基本數(shù)據(jù)類型, 如果是其他的類型,直接調(diào)用對應(yīng)類型的encode即可)
[aCoder encodeInteger:self.age forKey:kStudentAgeKey];
// 歸檔自定義類(Book)對象
[aCoder encodeObject:self.book forKey:kBookKey];
}
@end
/***************************************/
viewController.m
// 保存
- (IBAction)saveDatas:(id)sender {
// 創(chuàng)建對象
Student *student = [[Student alloc] init];
student.name = @"Alex";
student.age = 15;
student.book.bookName = @"習(xí)近平中國特色社會主義"; // 獲取文件路徑
NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *filePath = [path stringByAppendingPathComponent:@"student.archiver"]; // 將自定義的對象保存到文件中,調(diào)用的是NSKeyedArchiver的類方法
BOOL flag = [NSKeyedArchiver archiveRootObject:student toFile:filePath];
if (flag) {
NSLog(@"如果來到這里說明歸檔成功");
}
}
// 讀取
- (IBAction)readDatas:(id)sender {
// 獲取文件保存的路徑
NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *filePath = [path stringByAppendingPathComponent:@"student.archiver"];
// 打印文件保存的路徑
NSLog(@"%@", filePath);
// 從文件中讀取對象, 解檔對象就調(diào)用NSKeyedUnarchiver的一個類方法,unarchiveObjectWithFile: 即可
Student *student = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
if (student) {
// 來到這里說明讀取成功
NSLog(@"%@, %d, %@", student.name, student.age, student.book.bookName);
}
}
舉例4:






舉例5:







舉例6 - NSKeyedArchiver 簡單使用說明:
NSCoding協(xié)議聲明了兩個方法,這兩個方法都是必須實(shí)現(xiàn)的。一個用來說明如何將對象編碼到歸檔中,另一個說明如何進(jìn)行解檔來獲取一個新對象。
1.遵循NSCoding協(xié)議
@interface Person : NSObject <NSCoding>
2.設(shè)置屬性
@property (strong, nonatomic) UIImage *avatar;
@property (copy, nonatomic) NSString *name;
@property (assign, nonatomic) NSInteger age;
@end
3.實(shí)現(xiàn)協(xié)議方法
解檔:
- (id)initWithCoder:(NSCoder *)aDecoder {
if ([super init]) {
self.avatar = [aDecoder decodeObjectForKey:@"avatar"];
self.name = [aDecoder decodeObjectForKey:@"name"];
self.age = [aDecoder decodeIntegerForKey:@"age"];
}
return self;
}
歸檔:
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:self.avatar forKey:@"avatar"];
[aCoder encodeObject:self.name forKey:@"name"];
[aCoder encodeInteger:self.age forKey:@"age"];
}
特別注意:如果需要?dú)w檔的類是某個自定義類的子類時,就需要在歸檔和解檔之前先實(shí)現(xiàn)父類的歸檔和解檔方法。即 [super encodeWithCoder:aCoder]
和 [super initWithCoder:aDecoder] 方法;
其他使用說明:
需要把對象歸檔是調(diào)用NSKeyedArchiver的工廠方法 archiveRootObject: toFile: 方法:
NSString *file = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"person.data"];
Person *person = [[Person alloc] init];
person.avatar = self.avatarView.image;
person.name = self.nameField.text;
person.age = [self.ageField.text integerValue];
[NSKeyedArchiver archiveRootObject:person toFile:file];
需要從文件中解檔對象就調(diào)用NSKeyedUnarchiver的一個工廠方法 unarchiveObjectWithFile: 即可:
NSString *file = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"person.data"];
Person *person = [NSKeyedUnarchiver unarchiveObjectWithFile:file];
if (person) {
self.avatarView.image = person.avatar;
self.nameField.text = person.name;
self.ageField.text = [NSString stringWithFormat:@"%ld", person.age];
}
4.SQLite 與 FMDB
- SQLite
SQLite 是嵌入式關(guān)系型數(shù)據(jù)庫,是一個輕量級跨平臺的小型數(shù)據(jù)庫,是一種基于C語言開發(fā)的輕型數(shù)據(jù)庫,其最主要的特點(diǎn)就是輕量級、跨平臺,當(dāng)前很多嵌入式操作系統(tǒng)都將其作為數(shù)據(jù)庫首選。雖然 SQLite 是一款輕型數(shù)據(jù)庫,但是其功能也絕不亞于很多大型關(guān)系數(shù)據(jù)庫。SQLite3:操作數(shù)據(jù)比較快、可以局部讀取、比較小型,占用的內(nèi)存資源比較少。splite 可移植性比較高,有著和 MySpl 幾乎相同的數(shù)據(jù)庫語句,它的處理速度比 Mysql、PostgreSQL 這兩款著名的數(shù)據(jù)庫都還快,以及無需服務(wù)器即可使用的優(yōu)點(diǎn)。
1.在 iOS 中需要用 C 語言進(jìn)行對數(shù)據(jù)庫的操作、訪問(無法用 oc,因?yàn)?libsqlite3 框架基于 C 語言編寫)。
2.SQLite 采用動態(tài)數(shù)據(jù)類型。
3.要使用 SQLite 很簡單,如果在 Mac OSX 上使用可以考慮到 SQLite 官方網(wǎng)站下載命令行工具,也可以使用類似于 SQLiteManager、Navicat for SQLite 等工具。相關(guān)鏈接:https://juejin.cn/post/6844903793327341575
SQLite 常用命令:
啟動 SQLite:在終端鍵入 sqlite3,提示符變成 sqlite>
.help --- 顯示幫助信息
.quit/.exit --- 退出
.open --- 打開指定的數(shù)據(jù)庫文件
.save --- 將內(nèi)存中的數(shù)據(jù)庫保存到指定的文件
.databases --- 顯示數(shù)據(jù)庫的信息
.schema ?TABLE? --- 顯示表結(jié)構(gòu)
.tables ?PATTERN? --- 顯示所有的表
.mode MODE --- 設(shè)置輸出模式
.stats ON|OFF --- 開啟或關(guān)閉統(tǒng)計信息
.dump ?TABLE?--- 以SQL文本格式轉(zhuǎn)存數(shù)據(jù)庫
數(shù)據(jù)庫(SQLite)優(yōu)缺點(diǎn):
優(yōu)點(diǎn)
1.該方案可以存儲大量的數(shù)據(jù),存儲和檢索的速度非???。
2.能對數(shù)據(jù)進(jìn)行大量的聚合,這樣比起使用對象來講操作要快。缺點(diǎn)
1.它沒有提供數(shù)據(jù)庫的創(chuàng)建方式
2.它的底層是基于C語言框架設(shè)計的,沒有面向?qū)ο蟮?API,用起來非常麻煩。
3.發(fā)雜的數(shù)據(jù)模型的數(shù)據(jù)建表,非常麻煩。在實(shí)際開發(fā)中我們都是使用的是 FMDB 第三方開源的數(shù)據(jù)庫,該數(shù)據(jù)庫是基于 splite 封裝的面向?qū)ο蟮目蚣堋?/strong>

舉例 - 以學(xué)生表為例講解相關(guān)基本知識:
schema TbStudent查看學(xué)生表內(nèi)容
tables列出所有表
header on 顯示表頭
—插入數(shù)據(jù):
insert into TbStudent values(1001,'王大錘','男','重慶萬州','1990-1-6');
insert into TbStudent (stuid,stuname) values(1002,'李林’);//部分項(xiàng)
—查詢所有行所有列
select * from TbStudent;
—刪除數(shù)據(jù)(要加條件,否則刪除全部;下面同這)
delete from TbStudent where stuid=1004;
—更新數(shù)據(jù)
update TbStudent set stuaddr='四川綿陽',stubirth='1993.12.3' where stuid=1002;
(其中1002的沒有加地址和出生)
--投影操作
select stuname,stusex from TbStudent;
--投影時別名(as可省,別名用法)
select stuname as 姓名 , stusex as 性別 from Tbstudent;
--數(shù)據(jù)篩選(可加關(guān)系:and or)
select *from TbStudent where stusex='女';
select *from TbStudent where stusex='女' and stuaddr='南京';
select *from TbStudent where stusex='女' or stubirth='1998-1-6';
select *from TbStudent where stubirth between '1985-1-1' and '1990-12-12';
select *from TbStudent where stubirth<'1990-12-1';
--模糊查詢
select *from TbStudent where stuname like '王%';匹配任意多個字符
select *from TbStudent where stuname like'%王%';只要名字中有王字
select *from TbStudent where stuname like'王_';匹配精確一個字符
select *from TbStudent where stuname like'王__';
--排序(默認(rèn)是升序的asc(可?。唤敌騞esc)[同時用篩選和排序,先篩后排]
select *from TbStudent order by stubirth;
select *from TbStudent order by stubirth desc;
select *from TbStudent order by stusex, stubirth desc;//先排性別后排出生
--統(tǒng)計數(shù)
select count(stusex) 總?cè)藬?shù) from TbStudent;
--分組
select stusex,count(stusex)from TbStudent group by stusex;//男女各多少人
select stuaddr 家庭住址,count(stuaddr)as 總?cè)藬?shù) from TbStudent group by stuaddr order by 總?cè)藬?shù);
--聚合函數(shù)、分組函數(shù)
count
max
min
avg:平均數(shù)
sum
-----子查詢(把一個查詢的結(jié)果作為另一個查詢的一部分來使用)
select stuname, stubirth from TbStudent where stubirth=(select min (stubirth) from TbStudent);//年齡最大的名字
select min(stubirth) from TbStudent;//選擇出年齡最大
—分頁查詢(limit a,b—>a是第幾條,b是從這條開始依次查b個)
select * from tbemp order by sal desc limit 10,5;//第10到14
-------連接查詢(一次查詢多張表)-----
--->內(nèi)連接
--->外連接 ---> 左外連接(SQLite只支持左;即不管是否滿足連接,都寫出來,不滿足的地方補(bǔ)空)、右外連接、全外連接
//以TbDept表和TbEmp表為例:
(注意:連接兩張表時如果沒有連接條件將產(chǎn)生笛卡爾積)
寫法1:=
select empno,name,job,dname,dloc from TbEmp, Tbdept where TbEmp.dno =TbDept.deptno;
***
select empno,name,job,dname,dloc from TbEmp as t1, Tbdept as t2 where t1.dno =t2.deptno;
寫法2:… inner jion … on
select empno, name, job, dname, dloc from TbEmp, inner join
TbDept on dno = deptno;
***
select empno, name, job, dname, dloc from TbEmp as t1 inner join TbDept as t2 on t1.dno = t2.deptno;
***查詢薪水超過其所在部門平均薪水的員工的姓名、部門編號和工資
SELECT ename,deptno,sal from TbEmp as t1 inner join TbDept as t2 on sal>(SELECT avg(sal)from TbEmp where t1.dno=t2.deptno );
***查詢部門中薪水最高的人姓名、工資和所在部門名稱
SELECT ename,sal,dname from TbEmp as t1 inner join TbDept as t2 on sal=(SELECT max(sal) from TbEmp where t1.dno=t2.deptno);
***注意學(xué)會創(chuàng)建臨時表
寫在前面的是左表;寫在后面的右表
- FMDB
FMDB 是 iOS 平臺的 SQLite 數(shù)據(jù)庫框架,F(xiàn)MDB 以 OC 的方式封裝了 SQLite 的 C 語言 API 即基于 SQLite3 封裝的一套 OC 的 API 庫。因?yàn)?SQLite 的操作都是比較底層的 C 函數(shù)用起來比較麻煩,F(xiàn)MDB 對這些底層的東西進(jìn)行了面向?qū)ο蟮姆庋b,是一個非常好用的第三方庫,使用時需要添加蘋果提供的依賴庫 libsqlite3.dylib。FMDB的gitHub地址
其優(yōu)點(diǎn):
1.使用起來更加面向?qū)ο螅∪チ撕芏嗦闊?、冗余的C語言代碼。
2.對比蘋果自帶的 CoreData 框架,更加輕量級和靈活。
3.提供了多線程安全的數(shù)據(jù)庫操作方法,有效地防止數(shù)據(jù)混亂。相關(guān)鏈接:
https://juejin.cn/post/6844903805369188359
https://juejin.cn/post/7075274270472945671
FMDB 的核心 API:
FMDatabase:代表數(shù)據(jù)庫
FMResultSet:代表操作查詢結(jié)果的游標(biāo)
FMDatabaseAdditions:對 FMDatabase 類擴(kuò)展的 Category
FMDatabasePool:數(shù)據(jù)庫連接池支持類
FMDatabaseQueue:數(shù)據(jù)庫多線程操作支持類
FMDB 舉例講解:
2.例子講解
// 創(chuàng)建數(shù)據(jù)庫
- (void) createDb {
NSString *path = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/test.db"];
NSLog(@"%@", path);
self.db = [[FMDatabase alloc] initWithPath:path];
if([self.db open]) {
[self createTable];
}
else {
NSLog(@"創(chuàng)建數(shù)據(jù)庫失敗!");
}
}
// 創(chuàng)建表
- (void) createTable {
NSString *sql = @"create table if not exists tb_user (username varchar(20) primary key, password varchar(20) not null, email varchar(50));";
if([self.db executeUpdate:sql]) {
[self insertData];
}
else {
NSLog(@"創(chuàng)建用戶表失敗!");
}
}
// 插入數(shù)據(jù)
- (void) insertData {
NSString *sql = @"insert into tb_user values (?,?,?);";
if([self.db executeUpdate:sql, @"jackfrued", @"123456", @"jackfrued@126.com"]) {
}
else {
NSLog(@"插入數(shù)據(jù)失敗!");
}
}
// 顯示表中的記錄
- (void) showRecords {
NSString *path = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/test.db"];
self.db = [[FMDatabase alloc] initWithPath:path];
if([self.db open]) {
NSString *sql = @"select * from tb_user";
FMResultSet *rs = [self.db executeQuery:sql];
NSLog(@"%d", rs.next);
while([rs next]) {
NSLog(@"Username: %@", [rs stringForColumn:@"username"]);
NSLog(@"Password: %@", [rs stringForColumn:@"password"]);
NSLog(@"Email: %@", [rs stringForColumn:@"email"]);
}
[self clearData];
}
else {
NSLog(@"創(chuàng)建數(shù)據(jù)庫失敗!");
}
}
- (void) clearData {
NSString *sql = @"delete from tb_user";
if([self.db executeUpdate:sql]) {
NSLog(@"清理數(shù)據(jù)成功!!!");
}
}
- 數(shù)據(jù)庫補(bǔ)充說明
數(shù)據(jù)庫概念 (.db):
數(shù)據(jù)的集散地,有效地存儲和管理數(shù)據(jù)。App如果有大量的數(shù)據(jù)需要進(jìn)行本地存儲就可以考慮使用數(shù)據(jù)庫技術(shù),它不僅能夠有效的存儲和管理數(shù)據(jù),更重要的是提供了方便的檢索數(shù)據(jù)的手段。簡單的說,數(shù)據(jù)庫是實(shí)現(xiàn)數(shù)據(jù)持久化的重要方案,其他的方案包括:plist文件、archieve歸檔、NSUserDefaults、普通文件等。數(shù)據(jù)庫類型:
Oracle:甲骨文數(shù)據(jù)庫,當(dāng)今牛逼關(guān)系型數(shù)據(jù)庫
DB2:IBM公司的產(chǎn)品,數(shù)據(jù)倉庫和數(shù)據(jù)挖掘功能非常強(qiáng)大
SQLServer:微軟的產(chǎn)品,適合微軟自己的Windows服務(wù)器環(huán)境
MySQL:開源的數(shù)據(jù)庫產(chǎn)品,中小型站點(diǎn)的數(shù)據(jù)庫服務(wù)器首選關(guān)系型數(shù)據(jù)庫:比如 SQLite。
數(shù)據(jù)庫數(shù)據(jù):若只讀取不對其操作則保存在 Nsbundle,若對它要維護(hù)修改則放在沙箱中(數(shù)據(jù)要長久放在 doucument),創(chuàng)建數(shù)據(jù)庫放沙箱的 doucument文件。
關(guān)系型數(shù)據(jù)庫特點(diǎn):
1.用二維表組織數(shù)據(jù)(行:一條記錄;列:一條字段),主鍵為能夠唯一標(biāo)識一條記錄的字段(比如庫中已有的學(xué)號,再錄相同的不能錄),添加這個 primary key 表示它是主鍵。外鍵為其他表的主鍵(外來的主鍵),例如兩張表人與身份證例子,pragma foreign_key = on。
2.結(jié)構(gòu)化查詢語言(SQL)- SQL分4部分:
DDL:數(shù)據(jù)定義語言create(創(chuàng)建;可以加前綴命名—>如創(chuàng)建學(xué)生表: create Table TBstudent)/drop(刪除)/alter(修改)
DML:操作insert(添加)/delete(刪除)/update(更新)
DQL:查詢select (重點(diǎn))
DCL:控制grant/revoke1.關(guān)系代數(shù)
2.集合論
3.實(shí)體關(guān)系圖(ER圖),實(shí)體間關(guān)系:一對一:人與身份證、一對多:部門和員工、多對一:讀者與圖書
4.事務(wù) ACID,即:Atomic、Consistency、Isolation、Duration;要么全成功,要么全失敗;要么全做,要么全不做,以銀行轉(zhuǎn)賬為例。
5.CoreData
coreData 是蘋果官方在 iOS5 之后推出的綜合性數(shù)據(jù)庫,其使用了對象關(guān)系映射技術(shù),將對象轉(zhuǎn)換成數(shù)據(jù),將數(shù)據(jù)存儲在本地的數(shù)據(jù)庫中。coreData 為了提高效率需要將數(shù)據(jù)存儲在不同的數(shù)據(jù)庫中,比如在使用的時候,最好是將本地的數(shù)據(jù)保存到內(nèi)存中,這樣的目的是訪問速度比較快。CoreData 本身并不是一個并發(fā)安全的架構(gòu),所以在多線程中實(shí)現(xiàn) CoreData 會有問題。
Core Data (ORM:可以將關(guān)系模型和對象模型互轉(zhuǎn)),這個蘋果原生處理數(shù)據(jù)庫的非常難用,可選用第三方庫 ObjectRecord 和 MagicalRecord。各類應(yīng)用開發(fā)中只要牽扯到數(shù)據(jù)庫操作通常都會用到一個概念“對象關(guān)系映射(ORM)iOS 中 ORM 框架首選 Core Data,這是官方推薦的不需要借助第三方框架。無論是哪種平臺、哪種技術(shù),ORM 框架的作用都是相同的,那就是將關(guān)系數(shù)據(jù)庫中的表(準(zhǔn)確的說是實(shí)體)轉(zhuǎn)換為程序中的對象,其本質(zhì)還是對數(shù)據(jù)庫的操作(例如 Core Data 中如果存儲類型配置為 SQLite 則本質(zhì)還是操作的 SQLite 數(shù)據(jù)庫)。使用 Core Data 進(jìn)行數(shù)據(jù)庫存取并不需要手動創(chuàng)建數(shù)據(jù)庫,這個過程完全由 Core Data 框架完成,開發(fā)人員面對的是模型,主要的工作就是把模型創(chuàng)建起來,具體數(shù)據(jù)庫如何創(chuàng)建則不用管。
相關(guān)鏈接:
https://juejin.cn/post/7011708888718245925
http://t.zoukankan.com/HJiang-p-7818418.html

6.鑰匙串 Keychain
keychain 存儲在硬盤上,刪除了應(yīng)用,保存的數(shù)據(jù)還在。每個 APP 的 keychain 相對來說是獨(dú)立的,但是也可以實(shí)現(xiàn) APP 之間 keychain 數(shù)據(jù)的共享,前提是同一個 TeamID 下、且設(shè)置了數(shù)據(jù)共享。
Keychain 是 OS X 和 iOS 都提供一種安全地存儲敏感信息的工具,比如,存儲用戶ID,密碼,私鑰和證書等。存儲這些信息可以免除用戶重復(fù)輸入用戶名和密碼的過程。Keychain Services 的安全機(jī)制保證了存儲這些敏感信息不會被竊取。簡單說來,Keychain 就是一個安全容器。參考 apple 官網(wǎng)介紹
相關(guān)鏈接:
https://juejin.cn/post/6844903921765318669
https://juejin.cn/post/6952697138656575496
https://juejin.cn/post/6903710640817307655







7.云服務(wù) iCloud
iCloud 文檔存儲功能來滿足應(yīng)用數(shù)據(jù)云存儲的需求,用戶可以在自己 iCloud 賬號下的任何設(shè)備訪問或修改 App 的這部分?jǐn)?shù)據(jù)。iOS iCloud 存儲中,蘋果提供了三個功能:Key-value storage、iCloud Documents、CloudKit。iCloud 存儲空間就是蘋果設(shè)備 icloud 云端數(shù)據(jù)存儲的空間,屬于一種云存儲,一般會自帶 5GB 的存儲空間,若用戶需要更大的存儲空間,是可以選擇購買的。
說明:
- iCloud 可以在iOS 設(shè)備和 iPadOS 設(shè)備開啟、鎖定并連接到電源時,通過 Wi-Fi 自動備份您的設(shè)備,用戶可以使用 iCloud 云備份來恢復(fù) iOS 或 iPadOS 設(shè)備,或者無縫設(shè)置新設(shè)備。
2.查看在 iOS 設(shè)備、iPadOS 設(shè)備和 Mac 上打開的網(wǎng)站(iCloud 標(biāo)簽頁)。即使在離線狀態(tài)下,也可以閱讀閱讀列表中的文章。另外也可以在 iOS 設(shè)備、iPadOS 設(shè)備、Mac 和 Windows 電腦上使用相同的書簽。請參閱 Safari 瀏覽器中的 iCloud 標(biāo)簽頁、書簽和閱讀列表。
相關(guān)鏈接:
https://juejin.cn/post/6844903615723733000
https://baijiahao.baidu.com/s?id=1719371222823021907&wfr=spider&for=pc

8.YYCache 緩存
相關(guān)鏈接:
https://juejin.cn/post/6844903776113917966
https://juejin.cn/post/6885605205380562952
https://juejin.cn/post/6844903694341767182

9.bbx 司機(jī)端舉例
舉例1:新訂單有個提示語新字
場景1:在任務(wù)列表接到新訂單或者在 App 中收到新訂單推送的時候,在任務(wù)列表頁面訂單 cell 上面會提示有個新字,地圖頁面氣泡有個新字,點(diǎn)擊操作改訂單(滑動或者打電話、導(dǎo)航..)則表示已經(jīng)操作過不是新訂單了則去掉新字。這個通過判斷訂單 id 來區(qū)別,可以在訂單列表綁定的數(shù)據(jù)模型里面封裝:通過 id 添加到新訂單列表、判斷是否是新訂單、從新訂單 id 列表中移除指定訂單 id 等方法。




應(yīng)用1 - 判斷是否是新訂單:isNewOrder


應(yīng)用2 - 添加到新訂單列表:addIntoNewOrderList

應(yīng)用3 - 從新訂單 id 列表中移除指定訂單 id :removeFormNewOrderList


舉例2:成都機(jī)場提示
場景2:定位成都的司機(jī),未報班狀態(tài)下訪問首頁,新增“成都地區(qū)有新投運(yùn)機(jī)場”相關(guān)提示。
1.提示內(nèi)容:成都地區(qū)存在新投運(yùn)機(jī)場,請您在接到相關(guān)訂單后及時跟乘客確認(rèn)目的地,以免走錯機(jī)場。
2.展示邏輯:優(yōu)化上線后推送給定位為“成都”的司機(jī),司機(jī)非報班狀態(tài)下可見此提示,司機(jī)點(diǎn)擊“X”后隱藏不再展示橫幅。
3.注意2種情況:
第一種是同一部手機(jī)多次登錄同一個賬號或者同一部手機(jī)登錄多個賬號的時候,點(diǎn)擊 X 關(guān)閉提示語會記錄關(guān)閉次數(shù),展示的時候需要判斷當(dāng)前賬號與關(guān)閉賬號是否是同一個賬號。如果關(guān)閉次數(shù)大于0,判斷如果是同一個賬號則表示之前展示過且被關(guān)閉過則不需要展示,如果不是同一個賬號則表示關(guān)閉的是其他賬號,當(dāng)前賬號沒有被關(guān)閉過沒有關(guān)閉次數(shù)即未展示過或者展示過但未被關(guān)閉過,這個時候需要展示。
第二種情況是同一個賬號被多部手機(jī)登錄的時候,如果關(guān)閉次數(shù)等于0,即第一次進(jìn)來頁面未報班的時候都展示。即使賬號在 A 手機(jī)登錄展示過被關(guān)閉過,再在 B 手機(jī)上登錄一樣需要重新走 A 的邏輯,第一次進(jìn)來頁面未報班的時候還是需要展示提示語,即這個緩存以手機(jī)為準(zhǔn),不以賬號為準(zhǔn)。







舉例3:訂單有個提示標(biāo)簽
場景3:如果是新用戶即首次下單的用戶,對于貨件訂單且訂單備注包含類型“手機(jī)數(shù)碼、數(shù)碼產(chǎn)品、大物件、其它、其他”等,如果訂單狀態(tài)為上車了則不展示,如果是還沒有上車分2種情況,第一種是之前沒有展示過或者展示過沒有關(guān)閉則展示,第二種是之前展示過且被關(guān)閉過則不展示提示標(biāo)簽。
注意:
1.如果賬號在手機(jī) A 登錄有展示標(biāo)簽,點(diǎn)擊關(guān)閉后再在手機(jī) B 登錄發(fā)現(xiàn)也會有標(biāo)簽,即緩存跟手機(jī)有關(guān)跟賬號沒有關(guān)系,兩個手機(jī)獨(dú)立的,相當(dāng)于各自都是第一次走邏輯互不影響。
2.如果賬號在手機(jī)上登錄有展示標(biāo)簽,點(diǎn)擊關(guān)閉不展示標(biāo)簽。這個時候 App 卸載重新安裝,新裝的 App 相當(dāng)于第一次重新走邏輯會展示標(biāo)簽。


