可能碰到的iOS筆試面試題(14)--數據存儲

數據存儲

sqlite中插入特殊字符的方法和接收到處理方法。

除’其他的都是在特殊字符前面加“/”,而 ' -> '' 。方法:keyWord = keyWord.replace("/","http://");

什么是NSManagedObject模型?

NSManagedObjcet是NSObject的子類,Core Date的重要組成部分。是一個通用類,實現了Core Date模型層所需的基本功能,用戶可以通過NSManagedObjcet建立自己的數據模型。

你實現過多線程的Core Data么?NSPersistentStoreCoordinator,NSManagedObjectContext和NSManagedObject中的哪些需要在線程中創建或者傳遞?你是用什么樣的策略來實現的?

1>  CoreData是對SQLite數據庫的封裝
2>  coreData中有三個對象是必須掌握的, 
NSManagedObject :只要定義一個類繼承于該類就會創建一張與之對應的表, 也就是一個繼承于該類的類就對應一張表. 每一個通過繼承該類創建出來的對象,都是該類對應的表中的一條數據

NSManagedObjectContext: 用于操作數據庫, 只要有類它就能對數據庫的表進行增刪改查

NSPersistentStoreCoordinator: 決定數據存儲的位置 (SQLite/XML/其它文件中)
3>  Core data本身并不是一個并發安全的架構所以在多線程中實現Core data會有問題.問題在于
>2.1  CoreData中的NSManagedObjectContext在多線程中不安全
>2.2如果想要多線程訪問CoreData的話,最好的方法是一個線程一個NSManagedObjectContext
>2.3每個NSManagedObjectContext對象實例都可以使用同一個NSPersistentStoreCoordinator實例,這是因為NSManagedObjectContext會在便用NSPersistentStoreCoordinator前上鎖

簡單描述下客戶端的緩存機制?

  • 緩存可以分為:內存數據緩存、數據庫緩存、文件緩存
    • 每次想獲取數據的時候
    • 先檢測內存中有無緩存
    • 再檢測本地有無緩存(數據庫\文件)
    • 最終發送網絡請求
    • 將服務器返回的網絡數據進行緩存(內存、數據庫、文件), 以便下次讀取

什么是序列化和反序列化,用來做什么

  • 序列化把對象轉化為字節序列的過程
  • 反序列化把直接序列恢復成對象
  • 把對象寫到文件或者數據庫中,并且讀取出來

OC中實現復雜對象的存儲

  • 遵循NSCoding協議,實現復雜對象的存儲,實現該協議后可以對其進行打包或者解包,轉化為NSDate

iOS中常用的數據存儲方式有哪些?

  1. 數據存儲有四種方案,NSUserDefault,KeyChain,File,DB.
  2. 其中File有三種方式:plist,Archiver,Stream
  3. DB包括core Data和FMDB

說一說你對SQLite的認識

  • SQLite是目前主流的嵌入式關系型數據庫,其最主要的特點就是輕量級、跨平臺,當前很多嵌入式操作系統都將其作為數據庫首選。

  • 雖然SQLite是一款輕型數據庫,但是其功能也絕不亞于很多大型關系數據庫。

  • 學習數據庫就要學習其相關的定義、操作、查詢語言,也就是大家日常說得SQL語句。和其他數據庫相比,SQLite中的SQL語法并沒有太大的差別,因此這里對于SQL語句的內容不會過多贅述,大家可以參考SQLite中其他SQL相關的內容,這里還是重點講解iOS中如何使用SQLite構建應用程序。先看一下SQLite數據庫的幾個特點:

    • 基于C語言開發的輕型數據庫
    • 在iOS中需要使用C語言語法進行數據庫操作、訪問(無法使用ObjC直接訪問,因為libqlite3框架基于C語言編寫)
    • SQLite中采用的是動態數據類型,即使創建時定義了一種類型,在實際操作時也可以存儲其他類型,但是推薦建庫時使用合適的類型(特別是應用需要考慮跨平臺的情況時)
    • 建立連接后通常不需要關閉連接(盡管可以手動關閉)
  • 在iOS中操作SQLite數據庫可以分為以下幾步(注意先在項目中導入libsqlite3框架):

    • 打開數據庫,利用sqlite3_open()打開數據庫會指定一個數據庫文件保存路徑,如果文件存在則直接打開,否則創建并打開。打開數據庫會得到一個sqlite3類型的對象,后面需要借助這個對象進行其他操作。
    • 執行SQL語句,執行SQL語句又包括有返回值的語句和無返回值語句。
    • 對于無返回值的語句(如增加、刪除、修改等)直接通過sqlite3_exec()函數執行;
    • 對于有返回值的語句則首先通過sqlite3_prepare_v2()進行sql語句評估(語法檢測),然后通過sqlite3_step()依次取出查詢結果的每一行數據,對于每行數據都可以通過對應的sqlite3_column_類型()方法獲得對應列的數據,如此反復循環直到遍歷完成。當然,最后需要釋放句柄。

說一說你對FMDB的認識

  • FMDB是一個處理數據存儲的第三方框架,框架是對sqlite的封裝,整個框架非常輕量級但又不失靈活性,而且更加面向對象。FMDB有如下幾個特性:
    • FMDB既然是對于libsqlite3框架的封裝,自然使用起來也是類似的,使用前也要打開一個數據庫,這個數據庫文件存在則直接打開否則會創建并打開。這里FMDB引入了一個MFDatabase對象來表示數據庫,打開數據庫和后面的數據庫操作全部依賴此對象。
    • 對于數據庫的操作跟前面KCDbManager的封裝是類似的,在FMDB中FMDatabase類提供了兩個方法executeUpdate:和executeQuery:分別用于執行無返回結果的查詢和有返回結果的查詢。當然這兩個方法有很多的重載這里就不詳細解釋了。唯一需要指出的是,如果調用有格式化參數的sql語句時,格式化符號使用“?”而不是“%@”、等。
    • 我們知道直接使用libsqlite3進行數據庫操作其實是線程不安全的,如果遇到多個線程同時操作一個表的時候可能會發生意想不到的結果。為了解決這個問題建議在多線程中使用FMDatabaseQueue對象,相比FMDatabase而言,它是線程安全的。
    • 將事務放到FMDB中去說并不是因為只有FMDB才支持事務,而是因為FMDB將其封裝成了幾個方法來調用,不用自己寫對應的sql而已。其實在在使用libsqlite3操作數據庫時也是原生支持事務的(因為這里的事務是基于數據庫的,FMDB還是使用的SQLite數據庫),只要在執行sql語句前加上“begin transaction;”執行完之后執行“commit transaction;”或者“rollback transaction;”進行提交或回滾即可。另外在Core Data中大家也可以發現,所有的增、刪、改操作之后必須調用上下文的保存方法,其實本身就提供了事務的支持,只要不調用保存方法,之前所有的操作是不會提交的。在FMDB中FMDatabase有beginTransaction、commit、rollback三個方法進行開啟事務、提交事務和回滾事務。

說一說你對Core Data的認識

Core Data使用起來相對直接使用SQLite3的API而言更加的面向對象,操作過程通常分為以下幾個步驟:

  • 創建管理上下文
    創建管理上下可以細分為:加載模型文件->指定數據存儲路徑->創建對應數據類型的存儲->創建管理對象上下方并指定存儲。
    經過這幾個步驟之后可以得到管理對象上下文NSManagedObjectContext,以后所有的數據操作都由此對象負責。同時如果是第一次創建上下文,Core Data會自動創建存儲文件(例如這里使用SQLite3存儲),并且根據模型對象創建對應的表結構。
  • 查詢數據
    對于有條件的查詢,在Core Data中是通過謂詞來實現的。首先創建一個請求,然后設置請求條件,最后調用上下文執行請求的方法。
  • 插入數據
    插入數據需要調用實體描述對象NSEntityDescription返回一個實體對象,然后設置對象屬性,最后保存當前上下文即可。這里需要注意,增、刪、改操作完最后必須調用管理對象上下文的保存方法,否則操作不會執行。
  • 刪除數據
    刪除數據可以直接調用管理對象上下文的deleteObject方法,刪除完保存上下文即可。注意,刪除數據前必須先查詢到對應對象。
  • 修改數據
    修改數據首先也是取出對應的實體對象,然后通過修改對象的屬性,最后保存上下文。

OC中有哪些數據存儲方式,各有什么區別?

  • OC中有四種數據存儲方式:
    • NSUserDefaults,用于存儲配置信息
    • SQLite,用于存儲查詢需求較多的數據
    • CoreData,用于規劃應用中的對象
  • 使用基本對象類型定制的個性化緩存方案.
    • NSUserDefaults:對象中儲存了系統中用戶的配置信息,開發者可以通過這個實例對象對這些已有的信息進行修改,也 可以按照自己的需求創建新的配置項。
    • SQLite擅長處理的數據類型其實與NSUserDefaults差不多,也是基礎類型的小數據,只是從組織形式上不同。開發者可以以關系型數據庫的方式組織數據,使用SQL DML來管理數據。一般來說應用中的格式化的文本類數據可以存放在數據庫 中,尤其是類似聊天記錄、Timeline等這些具有條件查詢和排序需求的數據。
    • CoreData是一個管理方案,它的持久化可以通過SQLite、XML或二進制文件儲存。它可以把整個應用中的對象建模并進行自動化的管理。從歸檔文件還原模型時CoreData并不是一次性把整個模型中的所有數據都載入內存,而是根據運行時狀態,把被調用到的對象實例載入內存。框架會自動控制這個過程,從而達到控制內存消耗,避免浪費。 無論從設計原理還是使用方法上看,CoreData都比較復雜。因此,如果僅僅是考慮緩存數據這個需求,CoreData絕對不是一個優選方案。
    • CoreData的使用場景在于:整個應用使用CoreData規劃,把應用內的數據通過CoreData建模,完全基于CoreData架構應用。
    • 使用基本對象類型定制的個性化緩存方案:從需求出發分析緩存數據有哪些要求:按Key查找,快速讀取,寫入不影響正常操作,不浪費內存,支持歸檔。這些都是基本需求,那么再進一步或許還需要固定緩存項數量,支持隊列緩存,緩存過期等。
      數據存儲這一塊, 面試常問, 你常用哪一種數據存儲? 什么是序列化? sqlite是直接用它還是用封裝了它的第三方庫? 尤其是會問sqlite和core data的區別?

iOS平臺怎么做數據的持久化?coredata和sqlite有無必然聯系?coredata是一個關系型數據庫嗎?

  • iOS中可以有四種持久化數據的方式: 屬性列表、對象歸檔、SQLite3和Core Data

  • coredata可以使你以圖形界面的方式快速的定義app的數據模型,同時在你的代碼中容易獲取到它。

  • coredata提供了基礎結構去處理常用的功能,例如保存,恢復,撤銷和重做,允許你在app中繼續創建新的任務。

  • 在使用coredata的時候,你不用安裝額外的數據庫系統,因為coredata使用內置的sqlite數據庫。

  • coredata將你app的模型層放入到一組定義在內存中的數據對象。

  • coredata會 追蹤這些對象的改變,同時可以根據需要做相應的改變,例如用戶執行撤銷命令。

  • 當coredata在對你app數據的改變進行保存的時 候,core data會把這些數據歸檔,并永久性保存。

  • mac os x中sqlite庫,它是一個輕量級功能強大的關系數據引擎,也很容易嵌入到應用程序。可以在多個平臺使用,sqlite是一個輕 量級的嵌入式sql數據庫編程。

  • 與coredata框架不同的是,sqlite是使用程序式的,sql的主要的API來直接操作數據表。

  • Core Data不是一個關系型數據庫,也不是關系型數據庫管理系統(RDBMS)。

  • 雖然Core Dta支持SQLite作為一種存儲類型, 但它不能使用任意的SQLite數據庫。

  • Core Data在使用的過程種自己創建這個數據庫。Core Data支持對一、對多的關系。

如果后期需要增加數據庫中的字段怎么實現,如果不使用CoreData呢?

  • 編寫SQL語句來操作原來表中的字段
  • 增加表字段:ALTER TABLE 表名 ADD COLUMN 字段名 字段類型;
  • 刪除表字段:ALTER TABLE 表名 DROP COLUMN 字段名;
  • 修改表字段:ALTER TABLE 表名 RENAME COLUMN 舊字段名 TO 新字段名;

SQLite數據存儲是怎么用?

  • 添加SQLite動態庫:導入主頭文件:#import <sqlite3.h>
  • 利用C語言函數創建\打開數據庫,編寫SQL語句

簡單描述下客戶端的緩存機制?

  • 緩存可以分為:內存數據緩存、數據庫緩存、文件緩存
  • 每次想獲取數據的時候
  • 先檢測內存中有無緩存
  • 再檢測本地有無緩存(數據庫\文件)
  • 最終發送網絡請求
  • 將服務器返回的網絡數據進行緩存(內存、數據庫、文件)以便下次讀取

你實現過多線程的Core Data么?NSPersistentStoreCoordinator,NSManagedObjectContext和NSManagedObject中的哪些需要在線程中創建或者傳遞?你是用什么樣的策略來實現的?

  • CoreData是對SQLite數據庫的封裝
  • CoreData中的NSManagedObjectContext在多線程中不安全
  • 如果想要多線程訪問CoreData的話,最好的方法是一個線程一個NSManagedObjectContext
  • 每個NSManagedObjectContext對象實例都可以使用同一個NSPersistentStoreCoordinator實例,這是因為NSManagedObjectContext會在便用NSPersistentStoreCoordinator前上鎖

Core Data數據遷移

博客地址: http://blog.csdn.net/jasonblog/article/details/17842535

FMDB的使用和對多張表的處理

博客地址: http://blog.csdn.net/wscqqlucy/article/details/8464398

說說數據庫的左連接和右連接的區別

  • 數據庫左連接和右連接的區別:主表不一樣通過左連接和右連接,最小條數為3(記錄條數較小的記錄數),最大條數為12(3×4)

  • 技術博客的地址 : http://www.2cto.com/database/201407/317367.html

iOS 的沙盒目錄結構是怎樣的? App Bundle 里面都有什么?

  1. 沙盒結構

    • Application:存放程序源文件,上架前經過數字簽名,上架后不可修改
    • Documents:常用目錄,iCloud備份目錄,存放數據,這里不能存緩存文件,否則上架不被通過
    • Library
      • Caches:存放體積大又不需要備份的數據,SDWebImage緩存路徑就是這個
      • Preference:設置目錄,iCloud會備份設置信息
    • tmp:存放臨時文件,不會被備份,而且這個文件下的數據有可能隨時被清除的可能
  2. App Bundle 里面有什么

    • Info.plist:此文件包含了應用程序的配置信息.系統依賴此文件以獲取應用程序的相關信息
    • 可執行文件:此文件包含應用程序的入口和通過靜態連接到應用程序target的代碼
    • 資源文件:圖片,聲音文件一類的
    • 其他:可以嵌入定制的數據資源

你會如何存儲用戶的一些敏感信息,如登錄的 token

使用keychain來存儲,也就是鑰匙串,使用keychain需要導入Security框架

自定義一個keychain的類
#import <Security/Security.h>
@implementation YCKKeyChain

+(NSMutableDictionary *)getKeychainQuery:(NSString *)service {
return [NSMutableDictionary dictionaryWithObjectsAndKeys:
        (__bridge_transfer id)kSecClassGenericPassword,(__bridge_transfer   id)kSecClass,
        service, (__bridge_transfer id)kSecAttrService,
        service, (__bridge_transfer id)kSecAttrAccount,
        (__bridge_transfer id)kSecAttrAccessibleAfterFirstUnlock,   (__bridge_transfer id)kSecAttrAccessible,
        nil];
}

+(void)save:(NSString *)service data:(id)data {
// 獲得搜索字典
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
// 添加新的刪除舊的
SecItemDelete((__bridge_retained CFDictionaryRef)keychainQuery);
// 添加新的對象到字符串
[keychainQuery setObject:[NSKeyedArchiver archivedDataWithRootObject:data]  forKey:(__bridge_transfer id)kSecValueData];
// 查詢鑰匙串
SecItemAdd((__bridge_retained CFDictionaryRef)keychainQuery, NULL);
}

+(id)load:(NSString *)service {
id ret = nil;
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
// 配置搜索設置
[keychainQuery setObject:(id)kCFBooleanTrue forKey:(__bridge_transfer   id)kSecReturnData];
[keychainQuery setObject:(__bridge_transfer id)kSecMatchLimitOne forKey:    (__bridge_transfer id)kSecMatchLimit];
CFDataRef keyData = NULL;
if (SecItemCopyMatching((__bridge_retained CFDictionaryRef)keychainQuery, (CFTypeRef *)&keyData) == noErr) {
    @try {
        ret = [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge_transfer NSData *)keyData];
    } @catch (NSException *e) {
        NSLog(@"Unarchive of %@ failed: %@", service, e);
    } @finally {
    }
}
return ret;
}

+(void)delete:(NSString *)service {
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
SecItemDelete((__bridge_retained CFDictionaryRef)keychainQuery);
}

在別的類實現存儲,加載,刪除敏感信息方法
// 用來標識這個鑰匙串
static NSString * const KEY_IN_KEYCHAIN = @"com.yck.app.allinfo";
// 用來標識密碼
static NSString * const KEY_PASSWORD = @"com.yck.app.password";

+(void)savePassWord:(NSString *)password
{
NSMutableDictionary *passwordDict = [NSMutableDictionary dictionary];
[passwordDict setObject:password forKey:KEY_PASSWORD];
[YCKKeyChain save:KEY_IN_KEYCHAIN data:passwordDict];
}

+(id)readPassWord
{
NSMutableDictionary *passwordDict = (NSMutableDictionary *)[YCKKeyChain     load:KEY_IN_KEYCHAIN];
return [passwordDict objectForKey:KEY_PASSWORD];
}

+(void)deletePassWord
{
[YCKKeyChain delete:KEY_IN_KEYCHAIN];
}

使用 NSUserDefaults 時,如何處理布爾的默認值?(比如返回 NO,不知道是真的 NO 還是沒有設置過)

if([[NSUserDefaults standardUserDefaults] objectForKey:ID] == nil){
    NSLog(@"沒有設置");
}

MD5和Base64的區別是什么,各自使用場景是什么?

做過加密相關的功能的,幾乎都會使用到MD5和Base64,它們兩者在實際開發中是最常用的。
?   MD5:是一種不可逆的摘要算法,用于生成摘要,無法逆著破解得到原文。常用的是生成32位摘要,用于驗證數據的有效性。比如,在網絡請求接口中,通過將所有的參數生成摘要,客戶端和服務端采用同樣的規則生成摘要,這樣可以防篡改。又如,下載文件時,通過生成文件的摘要,用于驗證文件是否損壞。
?   Base64:屬于加密算法,是可逆的,經過encode后,可以decode得到原文。在開發中,有的公司上傳圖片采用的是將圖片轉換成base64字符串,再上傳。在做加密相關的功能時,通常會將數據進行base64加密/解密。

plist文件是用來做什么的。一般用它來處理一些什么方面的問題。

  • plist是iOS系統中特有的文件格式。我們常用的NSUserDefaults偏好設置實質上就是plist文件操作。plist文件是用來持久化存儲數據的。
  • 我們通常使用它來存儲偏好設置,以及那些少量的、數組結構比較復雜的不適合存儲數據庫的數據。比如,我們要存儲全國城市名稱和id,那么我們要優先選擇plist直接持久化存儲,因為更簡單。

當存儲大塊數據是怎么做?

  • 你有很多選擇,比如:
  • 使用NSUerDefaults
  • 使用XML, JSON, 或者 plist
  • 使用NSCoding存檔
  • 使用類似SQLite的本地SQL數據庫
  • 使用 Core Data
  • NSUserDefaults的問題是什么?雖然它很nice也很便捷,但是它只適用于小數據,比如一些簡單的布爾型的設置選項,再大點你就要考慮其它方式了
  • XML這種結構化檔案呢?總體來說,你需要讀取整個文件到內存里去解析,這樣是很不經濟的。使用SAX又是一個很麻煩的事情。
  • NSCoding?不幸的是,它也需要讀寫文件,所以也有以上問題。
  • 在這種應用場景下,使用SQLite 或者 Core Data比較好。使用這些技術你用特定的查詢語句就能只加載你需要的對象。在性能層面來講,SQLite和Core Data是很相似的。他們的不同在于具體使用方法。Core Data代表一個對象的graph model,但SQLite就是一個DBMS。Apple在一般情況下建議使用Core Data,但是如果你有理由不使用它,那么就去使用更加底層的SQLite吧。如果你使用SQLite,你可以用FMDB(https://GitHub.com/ccgus/fmdb)這個庫來簡化SQLite的操作,這樣你就不用花很多經歷了解SQLite的C API了

怎么解決sqlite鎖定的問題

1>  設置數據庫鎖定的處理函數
int sqlite3_busy_handler(sqlite3*, int(*)(void*,int), void*);
函數可以定義一個回調函數,當出現數據庫忙時,sqlite會調用該函數
當回調函數為NULL時,清除busy handle,申請不到鎖直接返回
回調函數的第二個函數會被傳遞為該由此次忙事件調用該函數的次數
回調函數返回非0,數據庫會重試當前操作,返回0則當前操作返回SQLITE_BUSY

2>  設定鎖定時的等待時間
int sqlite3_busy_timeout(sqlite3*, 60); 
定義一個毫秒數,當未到達該毫秒數時,sqlite會sleep并重試當前操作
如果超過ms毫秒,仍然申請不到需要的鎖,當前操作返回sqlite_BUSY
當ms<=0時,清除busy handle,申請不到鎖直接返回

文章如有問題,請留言,我將及時更正。

滿地打滾賣萌求贊,如果本文幫助到你,輕點下方的紅心,給作者君增加更新的動力。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 閱讀完本書,首先給我的感覺是內容有點對不起它的¥59.80定價,全書主要講了兩塊內容,一塊是SQLite3,...
    瑞小萌閱讀 2,877評論 4 33
  • *面試心聲:其實這些題本人都沒怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個offer,總結起來就是把...
    Dove_iOS閱讀 27,210評論 30 471
  • 發現 關注 消息 iOS 第三方庫、插件、知名博客總結 作者大灰狼的小綿羊哥哥關注 2017.06.26 09:4...
    肇東周閱讀 12,229評論 4 61
  • 此刻我正在地鐵上,在我視線可及的范圍內,至少十個人里有九個人低著頭看著刷著手機。朋友圈是個好地方,這里有詩和遠方,...
    劈柴捌哥閱讀 229評論 0 0
  • 今天,天氣晴朗,我坐在小窗前,看書,我窗簾拉開著窗簾,窗戶開著,坐在那里,溫暖的陽光照耀著我,微風輕輕的吹過。 我...
    一笑丿傾城閱讀 269評論 0 0