深受開發(fā)者喜愛的10大Core Data工具和開源庫
在iOS和OSX應用程序中存儲和查詢數(shù)據(jù),Core Data是一個很好的選擇。它不僅可以減少內(nèi)存使用、提高性能,而且它可以使你避免寫很多不必要的樣板文件代碼。
此外,Core Data API非常靈活,可以用在各種應用程序中,所有應用程序有不同的數(shù)據(jù)存數(shù)需求。
然而,這種靈活性意味著有時Core Data用起來可能稍微有點困難。即便你是一個Core Data專家,仍然會需要處理一些平常的任務,也會有很多犯錯的可能性。
幸運的是,有很多很好的工具可以幫你解決問題,讓Core Data更易于使用。為此我們選出了10個你應該知道和喜歡的工具和開源庫。
注意:即便有這些優(yōu)秀的工具和庫,你仍需要很好地理解Core Data。如果你需要獲得Core Data的更多經(jīng)驗,下載查閱我們的新手教程。
還要注意的是這篇文章以Objective-C為主,因為大部分Core Data庫都是用Objective-C寫的。如果你想學習怎樣用Swift來使用Core Data,可下載查閱我們已經(jīng)出版的書Core Data by Tutorials,這本書已經(jīng)完全為iOS 8和Swift更新了!
#10. RestKit
RestKit是為了和RESTful web服務交互的一個Objective-C框架。它提供了一個Core Data實體映射引擎,把序列化的響應對象直接映射給托管的對象。
下面的代碼例子展示了如何設置RestKit來訪問 OpenWeatherMap API,同時把/weather端點的JSON響應映射成為一個WFWeather 托管對象。
- - (void)loadForecastData {
- RKManagedObjectStore *store = self.managedObjectStore;
- // 1
- RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:@"WFWeather"
- inManagedObjectStore:store];
- [mapping addAttributeMappingsFromArray:@[@"temp", @"pressure", @"humidity"]];
- // 2
- NSIndexSet *statusCodeSet = RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful);
- RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor
- responseDescriptorWithMapping:mapping
- method:RKRequestMethodGET
- pathPattern:@"/data/2.5/weather"
- keyPath:@"main"
- statusCodes:statusCodeSet];
- // 3
- NSURL *url = [NSURL URLWithString:
- [NSString stringWithFormat:@"http://api.openweathermap.org/data/2.5/weather?q=Orlando"]];
- NSURLRequest *request = [NSURLRequest requestWithURL:url];
- RKManagedObjectRequestOperation *operation = [[RKManagedObjectRequestOperation alloc]
- initWithRequest:request
- responseDescriptors:@[responseDescriptor]];
- operation.managedObjectCache = store.managedObjectCache;
- operation.managedObjectContext = store.mainQueueManagedObjectContext;
- // 4
- [operation setCompletionBlockWithSuccess:
- ^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult){
- NSLog(@"%@",mappingResult.array);
- [self.tableView reloadData];
- } failure:^(RKObjectRequestOperation *operation, NSError *error) {
- NSLog(@"ERROR: %@", [error localizedDescription]);
- }];
- [operation start];
- }
代碼分解:
1、首先,創(chuàng)建一個RKEntityMapping對象來告訴RestKit如何把API的響應映射給WFWeather屬性。
2、這里,RKResponseDescriptor 把上面從/data/2.5/weather到 RKEntityMapping 實例的響應聯(lián)系在了一起。
3、RKManagedObjectRequestOperation 定義了要執(zhí)行的操作。這個例子中,你從OpenWeatherMap API請求了Orlando(奧蘭多)的天氣,然后把響應指向上面提到的RKResponseDescriptor 實例。
4、***,用需要的成功和失敗的block(塊)執(zhí)行操作。當RestKit看到和定義的RKResponseDescriptor 想匹配的應答返回的時候,它將直接把數(shù)據(jù)映射到WFWeather 實例上。
以上代碼不需要手動JSON解析、檢查[NSNull null]、手動創(chuàng)建Core Data實體,或者連接一個API時任何其他必須做的事情。RestKit通過一個簡單的映射字典把API響應轉換成Core Data模型對象。沒有比這個更容易的了。
要學習如何安裝和使用RestKit,請下載查閱我們的 RestKit教程說明。
#9. MMRecord
MMRecord是一個基于block(塊)的集成庫,它使用Core Data模型配置,自動創(chuàng)建并填充來自API響應的完整地對象圖。當它在后臺為你創(chuàng)建、獲取、填充NSManagedObjects 實例時,使從web服務請求來的生成的本地對象盡可能簡單。
它使得從web服務請求生成本地對象,像它在后臺創(chuàng)建、獲取、填充NSManagedObjects 實例那樣簡單。
下面的代碼塊,展示了怎樣使用MMRecord來進行相同的Orlando天氣調(diào)用,以及你在上面的RestKit例子中做的數(shù)據(jù)映射:
- NSManagedObjectContext *context = [[MMDataManager sharedDataManager] managedObjectContext];
- [WFWeather
- startPagedRequestWithURN:@"data/2.5/weather?q=Orlando"
- data:nil
- context:context
- domain:self
- resultBlock:^(NSArray *weather, ADNPageManager *pageManager, BOOL *requestNextPage) {
- NSLog(@"Weather: %@", weather);
- }
- failureBlock:^(NSError *error) {
- NSLog(@"%@", [error localizedDescription]);
- }];
無需編寫任何復雜的網(wǎng)絡代碼,或者手動解析JSON響應,你已經(jīng)調(diào)用一個API,并且已用幾行代碼的響應數(shù)據(jù)填充了Core Data托管對象。
MMRecord 如何知道你在API響應中定位你的對象?你的托管對象必須是MMRecord 的子類,然后像下面所示那樣重載keyPathForResponseObject :
- @interface WFWeather : MMRecord
- @property (nonatomic) float temp;
- @property (nonatomic) float pressure;
- @property (nonatomic) float humidity;
- @end
- @implementation WFWeather
- @dynamic temp;
- @dynamic pressure;
- @dynamic humidity;
- + (NSString *)keyPathForResponseObject {
- return @"main";
- }
- @end
keyPathForResponseObject 返回了一個關鍵路徑,指定了來自API的響應對象的根相關的對象的位置。這個例子中,關鍵路徑是data/2.5/weather 調(diào)用的main。
神奇之處不止這一點-MMRecord要求你創(chuàng)建一個服務器類,這個類知道如何請求你集成的API。謝天謝地,MMRecord附帶一個基于AFNetworking 的服務器類的例子。
關于配置和使用MMRecord的信息,MMRecord Github repository 的說明文件是***的開始。
#8. Magical Record
靈感來源于Ruby on Rails' ActiveRecord 系統(tǒng),提供了一系列支持一行實體的獲取、嵌入和刪除操作的類和類別。
下圖是運行的MagicalRecord視圖:
- // Fetching NSArray *people = [Person MR_findAll];
- // Creating Person *myPerson = [Person MR_createEntity];
- // Deleting [myPerson MR_deleteEntity];
MagicalRecord讓設置Core Data堆棧變得非常容易。而不是使用很多行的樣板代碼,你可以像下邊這樣只用AppDelegate文件里的一個方法調(diào)用來設置一個完整的Core Data堆棧。
|
|
在application:didFinishLaunchingWithOptions: 里,用你SQLite文件的名調(diào)用setupCoreDataStackWithStoreNamed。 這就建立了NSPersistentStoreCoordinator、NSManagedObjectModel 和NSManagedObjectContext 的實例,這樣你就可以準備使用Core Data工作了。
進一步地了解如何安裝和使用MagicalRecord,請參看我們的MagicalRecord 教程。
#7. GDCoreDataConcurrencyDebugging
并發(fā)問題是在Core Data中調(diào)試最難的事情。performBlock API可以幫忙,但仍然很容易犯錯。
你可以將開源項目GDCoreDataConcurrencyDebugging添加到自己的項目中,當在錯誤線程或者調(diào)度隊列上訪問NSManagedObjects時,它通過控制臺消息提醒你。
以下是在錯誤環(huán)境中訪問NSManagedObject 實例的例子:
- __block NSManagedObject *objectInContext1 = nil;
- [context1 performBlockAndWait:^{
- objectInContext1 = [[NSManagedObject alloc] initWithEntity:entity
- insertIntoManagedObjectContext:context1];
- objectInContext1.name = @"test";
- NSError *saveError;
- if ([context1 save:&saveError] == NO) {
- NSLog(@"Error: %@", [saveError localizedDescription]);
- }
- }];
- // Invalid access
- [context2 performBlockAndWait:^{
- NSString *name = objectInContext1.name;
- }];
在上面的代碼中,你正嘗試從一個對象中context2 讀取name, 這個對象最初在context1 被創(chuàng)建。
如果你使用GDCoreDataConcurrencyDebugging 運行上面的例子,你會看到下面的控制臺消息,通知你有問題發(fā)生:
2014-06-17 13:20:24.530 SampleApp[24222:60b] CoreData concurrency failure
注意:在你的應用程序上傳到App Store上去時,你得移除掉GDCoreDataConcurrencyDebugging ,它增加了少量的開銷,那些開銷無需存在于在發(fā)布的應用中。
在iOS 8和OS X Yosemite中,Core Data現(xiàn)在有檢測并發(fā)問題的能力。想要啟用這個新功能,你可以在啟動時通過Xcode?s Scheme Editor 把-com.apple.CoreData.ConcurrencyDebug 1 傳給你的應用程序。
然而,直到你逐步淘汰對OS更早版本的支持,GDCoreDataConcurrencyDebugging 都會在開發(fā)過程中一直通知你并發(fā)問題。
GDCoreDataConcurrencyDebugging README on Github 是安裝和使用這個工具***的資源信息。
#6. CoreData-hs
CoreData-hs 生成類別方法來執(zhí)行Core Data模型里所有實體和屬性的常見獲取請求。創(chuàng)建這些方法不難,但它耗時--編碼時節(jié)省每一點時間都是有價值的!
比如,你的天氣應用程序里有一個查看天氣預報和使用帶有timeStamp,temp和 summary 屬性的WFForecast 實體來模仿每天的預報的視圖,CoreData-hs 將為你創(chuàng)建以下的類別:
- #import #import @interface WFForecast (Fetcher)
- + (NSArray *)summaryIsEqualTo:(id)object inContext:(NSManagedObjectContext *)context sortDescriptors:(NSArray *)sort error:(void(^)(NSError *error))errorBlock;
- + (NSArray *)summaryIsLessThan:(id)object inContext:(NSManagedObjectContext *)context sortDescriptors:(NSArray *)sort error:(void(^)(NSError *error))errorBlock;
- + (NSArray *)summaryIsGreaterThan:(id)object inContext:(NSManagedObjectContext *)context sortDescriptors:(NSArray *)sort error:(void(^)(NSError *error))errorBlock;
- + (NSArray *)summaryIsGreaterThanOrEqualTo:(id)object inContext:(NSManagedObjectContext *)context sortDescriptors:(NSArray *)sort error:(void(^)(NSError *error))errorBlock;
- + (NSArray *)summaryIsLessThanOrEqualTo:(id)object inContext:(NSManagedObjectContext *)context sortDescriptors:(NSArray *)sort error:(void(^)(NSError *error))errorBlock;
- + (NSArray *)summaryIsNotEqualTo:(id)object inContext:(NSManagedObjectContext *)context sortDescriptors:(NSArray *)sort error:(void(^)(NSError *error))errorBlock;
- + (NSArray *)summaryIsBetwixt:(id)object inContext:(NSManagedObjectContext *)context sortDescriptors:(NSArray *)sort error:(void(^)(NSError *error))errorBlock;
- + (NSArray *)tempIsEqualTo:(id)object inContext:(NSManagedObjectContext *)context sortDescriptors:(NSArray *)sort error:(void(^)(NSError *error))errorBlock;
- + (NSArray *)tempIsLessThan:(id)object inContext:(NSManagedObjectContext *)context sortDescriptors:(NSArray *)sort error:(void(^)(NSError *error))errorBlock;
- + (NSArray *)tempIsGreaterThan:(id)object inContext:(NSManagedObjectContext *)context sortDescriptors:(NSArray *)sort error:(void(^)(NSError *error))errorBlock;
- + (NSArray *)tempIsGreaterThanOrEqualTo:(id)object inContext:(NSManagedObjectContext *)context sortDescriptors:(NSArray *)sort error:(void(^)(NSError *error))errorBlock;
- + (NSArray *)tempIsLessThanOrEqualTo:(id)object inContext:(NSManagedObjectContext *)context sortDescriptors:(NSArray *)sort error:(void(^)(NSError *error))errorBlock;
- + (NSArray *)tempIsNotEqualTo:(id)object inContext:(NSManagedObjectContext *)context sortDescriptors:(NSArray *)sort error:(void(^)(NSError *error))errorBlock;
- + (NSArray *)tempIsBetwixt:(id)object inContext:(NSManagedObjectContext *)context sortDescriptors:(NSArray *)sort error:(void(^)(NSError *error))errorBlock;
- + (NSArray *)timeStampIsEqualTo:(id)object inContext:(NSManagedObjectContext *)context sortDescriptors:(NSArray *)sort error:(void(^)(NSError *error))errorBlock;
- + (NSArray *)timeStampIsLessThan:(id)object inContext:(NSManagedObjectContext *)context sortDescriptors:(NSArray *)sort error:(void(^)(NSError *error))errorBlock;
- + (NSArray *)timeStampIsGreaterThan:(id)object inContext:(NSManagedObjectContext *)context sortDescriptors:(NSArray *)sort error:(void(^)(NSError *error))errorBlock;
- + (NSArray *)timeStampIsGreaterThanOrEqualTo:(id)object inContext:(NSManagedObjectContext *)context sortDescriptors:(NSArray *)sort error:(void(^)(NSError *error))errorBlock;
- + (NSArray *)timeStampIsLessThanOrEqualTo:(id)object inContext:(NSManagedObjectContext *)context sortDescriptors:(NSArray *)sort error:(void(^)(NSError *error))errorBlock;
- + (NSArray *)timeStampIsNotEqualTo:(id)object inContext:(NSManagedObjectContext *)context sortDescriptors:(NSArray *)sort error:(void(^)(NSError *error))errorBlock;
- + (NSArray *)timeStampIsBetwixt:(id)object inContext:(NSManagedObjectContext *)context sortDescriptors:(NSArray *)sort error:(void(^)(NSError *error))errorBlock;
- + (NSArray *)summaryIsLike:(id)object inContext:(NSManagedObjectContext *)context sortDescriptors:(NSArray *)sort error:(void(^)(NSError *error))errorBlock;
- + (NSArray *)summaryContains:(id)object inContext:(NSManagedObjectContext *)context sortDescriptors:(NSArray *)sort error:(void(^)(NSError *error))errorBlock;
- + (NSArray *)summaryMatches:(id)object inContext:(NSManagedObjectContext *)context sortDescriptors:(NSArray *)sort error:(void(^)(NSError *error))errorBlock;
- + (NSArray *)summaryBeginsWith:(id)object inContext:(NSManagedObjectContext *)context sortDescriptors:(NSArray *)sort error:(void(^)(NSError *error))errorBlock;
- + (NSArray *)summaryEndsWith:(id)object inContext:(NSManagedObjectContext *)context sortDescriptors:(NSArray *)sort error:(void(^)(NSError *error))errorBlock;
- + (NSArray *)tempIsLike:(id)object inContext:(NSManagedObjectContext *)context sortDescriptors:(NSArray *)sort error:(void(^)(NSError *error))errorBlock;
- + (NSArray *)tempContains:(id)object inContext:(NSManagedObjectContext *)context sortDescriptors:(NSArray *)sort error:(void(^)(NSError *error))errorBlock;
- + (NSArray *)tempMatches:(id)object inContext:(NSManagedObjectContext *)context sortDescriptors:(NSArray *)sort error:(void(^)(NSError *error))errorBlock;
- + (NSArray *)tempBeginsWith:(id)object inContext:(NSManagedObjectContext *)context sortDescriptors:(NSArray *)sort error:(void(^)(NSError *error))errorBlock;
- + (NSArray *)tempEndsWith:(id)object inContext:(NSManagedObjectContext *)context sortDescriptors:(NSArray *)sort error:(void(^)(NSError *error))errorBlock;
- @end
#p#
正如你看到的,生成了很多方法!以下是tempIsGreaterThan:inContext:sortDescriptors: error: 生成的實現(xiàn)方法:
- + (NSArray *)tempIsGreaterThan:(id)object inContext:(NSManagedObjectContext *)context sortDescriptors:(NSArray *)sort error:(void(^)(NSError *error))errorBlock {
- NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"WFForecast"];
- [fetchRequest setPredicate:[NSPredicate predicateWithFormat:@"temp > %@", object]];
- [fetchRequest setSortDescriptors:sort];
- NSError *err = nil;
- NSArray *results = [context executeFetchRequest:fetchRequest error:&err];
- if(!results && errorBlock) {
- errorBlock(err);
- return nil;
- }
- return results;
- }
方法一旦生成,你就可以用它們通過特定條件獲取請求。比如,需要獲取所有溫度超過70°的WFForecast對象,你可以調(diào)用tempIsGreaterThan:inContext:sortDescriptors:error:,然后簡單地傳遞目標溫度,如下所示:
- NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"temp" ascending:YES];
- NSArray *results = [WFForecast tempIsGreaterThan:@(70)
- inContext:self.managedObjectContext
- sortDescriptors:@[sortDescriptor]
- error:^(NSError *error) {
- NSLog(@"Error: %@", [error localizedDescription]);
- }];
你將返回一個匹配對象的數(shù)據(jù)。
CoreData-hs 是一個輕量級的工具,如果你傾向于手動生成大量這種類型的請求,它可以節(jié)省你的時間。關于安裝和使用說明,請查閱README on Github。
#5. Core Data Editor
你可以在Core Data Editor的GUI中查看和編輯APP基于Core Data的數(shù)據(jù)模型,它支持XML、二進制和SQLite持久性存儲類型。除了可以編輯基本屬性,你還可以編輯和形象化數(shù)據(jù)關系。你也可以在使用Core Data Editor的同時使用Mogenerator工具 (第#2項中討論)來創(chuàng)建模型代碼。
Core Data Editor熟悉蘋果的模式,展示不帶Z前綴的數(shù)據(jù),如果你曾看到Core Data生成的SQL文件,這個前綴你可能會熟悉。你可以在一個不錯的表格格式里瀏覽應用程序數(shù)據(jù)庫里的內(nèi)容。它也支持預覽二進制數(shù)據(jù),比如圖片,以及用一個標準日期選擇器在線編輯數(shù)據(jù)。
如果你想創(chuàng)建一個種子文件或者只想導入數(shù)據(jù),Core Data Editor可以生成一個CSV文件,把它轉換成Core Data里持久化對象,如下所示:
安裝Core Data Editor,從Thermal Core website 下載免費試用版。解壓下載的ZIP歸檔文件,把Core Data Editor.app 文件移動到你的Applications 目錄里。這個應用的作者最近也把它開源了,如果你想了解它是如何工作的,以及提高自己,你可以去學習下。
***次啟動應用程序,它會引導你完成一個簡短的設置過程。這個過程是可選的,但如果你至少指定iPhone模擬器目錄和Xcode歸檔數(shù)據(jù)的目錄,它將會加快速度。
注意:因為你得在GUI中選擇導出的數(shù)據(jù)和模擬器目錄,所以你可能在使用OS X Lion默認設置時會遇到問題,隱藏你的庫文件夾。
在Mavericks OS X里,你可以通過在Finder的主目錄里,選擇View / Show View Options ,檢查Show Library Folder來糾正這個問題。在Lion和Mountain Lion OS X中,同樣的事情可以通過在終端輸入chflags nohidden ~/Library/ 來完成。
更多有關Core Data的詳情可以在這個網(wǎng)站Thermal Core's website上找到。
#4. SQLite3
有時當調(diào)試一個棘手的數(shù)據(jù)問題時,在底層的Core Data SQLite數(shù)據(jù)庫中直接執(zhí)行SQL查詢是有幫助的。如果你沒有擴展的數(shù)據(jù)庫經(jīng)驗,這個可能不適合你。
使用SQLite3,首先打開Terminal終端,并導航到你應用程序的Documents 目錄。根據(jù)你的安裝情況,Documents目錄類似于 ~/Library/Application Support/iPhone Simulator/7.1-64/Applications/{your app's ID}/Documents.
更改上面命名中7.1-64 來匹配你使用的模擬器的版本。{your app's ID} 由Xcode自動生成,并唯一地標識應用的安裝。沒有簡單的辦法找出哪個ID是你的。你可以在創(chuàng)建Core Data堆棧的時候添加日志記錄,也可以尋找最近被多次修改的目錄--這將是你當前工作的應用程序。
文檔目錄將包含一個擴展sqlite的文件,這個就是你應用程序的數(shù)據(jù)庫文件。對于使用蘋果Core Data模板的應用程序而言,文件名稱要匹配你的應用的名字。用如下所示的SQLite3程序打開這個文件(這里的例子應用叫AddressBook,你的文件名字會有所不同):
1
|
$ sqlite3 AddressBook.sqlite
|
你將在控制臺看到以下提示:
- SQLite version 3.7.13 2012-07-17 17:46:21
- Enter ".help" for instructions
- Enter SQL statements terminated with a ";"
- sqlite>
現(xiàn)在準備好對數(shù)據(jù)庫執(zhí)行標準的SQL查詢。
比如,為了看到Core Data 使用的模式,可執(zhí)行以下的命令:
|
SQLite用模式里的一個文本列表清單來響應你的查詢,如下所示:
- table|ZMDMPERSON|ZMDMPERSON|3|CREATE TABLE ZMDMPERSON ( Z_PK INTEGER PRIMARY KEY, Z_ENT INTEGER, Z_OPT INTEGER, ZISNEW INTEGER, ZFIRSTNAME VARCHAR )
- table|Z_PRIMARYKEY|Z_PRIMARYKEY|4|CREATE TABLE Z_PRIMARYKEY (Z_ENT INTEGER PRIMARY KEY, Z_NAME VARCHAR, Z_SUPER INTEGER, Z_MAX INTEGER) table|Z_METADATA|Z_METADATA|5|CREATE TABLE Z_METADATA (Z_VERSION INTEGER PRIMARY KEY, Z_UUID VARCHAR(255), Z_PLIST BLOB)
- sqlite>
所有在列表欄里的Z前綴是Core Data底層使用SQLite的一部分。出于分析的目的,可以忽略它們。
注意:你不能直接寫入SQLite Core Data數(shù)據(jù)庫。蘋果可以隨時修改底層結構。
如果你真的有需要在生產(chǎn)應用程序中直接操作SQLite數(shù)據(jù),你應該放棄Core Data,用原始的SQL訪問。有幾種流行的框架可以幫助你管理你應用中的SQL實現(xiàn),包括FMDB和 FCModel。
如果只是分析數(shù)據(jù),到處訪問SQLite數(shù)據(jù)庫文件是沒什么關系的--只要不修改它的內(nèi)容。直接使用SQL分析數(shù)據(jù)的一個例子是不同屬性進行分組和計算,以理解屬性的差異。
舉個例子,如果你有一個地址薄的應用例子,想知道在每個城市里有多少聯(lián)系人生活,你可以用SQLite3提示執(zhí)行以下命令:
- SELECT t0.ZCITY, COUNT( t0.ZCITY ) FROM ZMDMPERSON t0 GROUP BY t0.ZCITY
SQLite會用地址薄數(shù)據(jù)庫里每個不同城市的數(shù)據(jù)來作出響應,如下面的例子所示:
- San Diego|23
- Orlando|34
- Houston|21
退出SQLite3終端程序,簡單地執(zhí)行以下的命令:
- sqlite> .exit
想要了解更多SQLite3的信息,可打開終端查看man 頁,并執(zhí)行man sqlite3命令。
#3. MDMCoreData
MDMCoreData(免責聲明--這個庫是我的寫的?。┦且粋€開源類的集合,讓使用Core Data變得更簡單。它并不試圖隱藏或抽象Core Data,而是執(zhí)行***實踐,以及減少所要的模板代碼。這比Xcode Core Data模板是一個更好的選擇。
MDMCoreData由以下四個類組成:
-
MDMPersistenceController,一個便利的控制器,支持創(chuàng)建多個child-managed object context 來建立一個高效的Core Data堆棧。它有一個內(nèi)置的私有 managed object context,異步保存到SQLite倉庫。
-
MDMFetchedResultsTableDataSource,獲取ResultsController delegate和列表數(shù)據(jù)源。
-
MDMFetchedResultsCollectionDataSource,獲取ResultsController delegate和集合數(shù)據(jù)源。
-
NSManagedObject+MDMCoreDataAdditions,一個托管對象類別,它提供輔助方法來消除模板代碼,比如實體名。
MDMCoreData一個***的特點是,它伴隨著一個支持表數(shù)據(jù)源的Core Data--所以你不必擔心要自己去實現(xiàn)。
#p#
你可以只設置MDMFetchedResultsTableDataSource實例的表數(shù)據(jù)源,而不是實現(xiàn)UITableViewDataSource 和NSFetchedResultsControllerDelegate 協(xié)議需要的所有方法。
當實例化MDMFetchedResultsTableDataSource 對象,你只要簡單地在表視圖里簡單傳送,并獲取一個results controller:
- - (void)viewDidLoad {
- [super viewDidLoad];
- self.tableDataSource = [[MDMFetchedResultsTableDataSource alloc] initWithTableView:self.tableView
- fetchedResultsController:[self fetchedResultsController]];
- self.tableDataSource.delegate = self;
- self.tableDataSource.reuseIdentifier = @"WeatherForecastCell";
- self.tableView.dataSource = self.tableDataSource;
- }
MDMFetchedResultsTableDataSource 有一個代理,有兩個必須實現(xiàn)的方法。一個方法為你的表格配置cell:
- - (void)dataSource:(MDMFetchedResultsTableDataSource *)dataSource configureCell:(id)cell
- withObject:(id)object {
- OWMForecast *forecast = object;
- UITableViewCell *tableCell = (UITableViewCell *)cell; tableCell.textLabel.text = forecast.summary; tableCell.detailTextLabel.text = forecast.date;
- }
第二個方法處理刪除操作:
- - (void)dataSource:(MDMFetchedResultsTableDataSource *)dataSource deleteObject:(id)object
- atIndexPath:(NSIndexPath *)indexPath {
- [self.persistenceController.managedObjectContext deleteObject:object];
- }
實現(xiàn)兩個所需的MDMFetchedResultsTableDataSource 方法,遠比實現(xiàn)表數(shù)據(jù)源所有所需方法和獲取results controller protocols要簡單的多。
你可以在MDMCoreData Github repository 找到更多關于MDMCoreData的信息。
#2. Mogenerator
因為Core Data完全支持鍵/值編碼(KVC)和鍵/值觀察(KVO),就沒必要實現(xiàn)自定義的NSManagedObject 類。當對你讀寫實體時,可以使用setValue:forKey: 和setValue:forKey: 。但這往往變得復雜和難于調(diào)試,因為字符串在編譯的時候沒法檢查正確性。
比如,你有個person 的Core Data實體,你可以像這樣讀寫屬性:
- NSString *personName = [person valueForKey:@"firstName"];
- [person setValue:@"Ned" forKey:@"firstName"];
上面的person 對象是一個屬性名為firstName 的NSManagedObject 實例。想要讀取firstName,你得用firstName 作為valueForKey:關鍵字。類似地,你可以使用setValue:forKey: 設置一個person 對象的first name。
更好的方法就是使用標準的訪問方法或者點語法。然而,要這么做的話你必須為你的實體實現(xiàn)NSManagedObject一個自定義子類。你可以添加邏輯模型,比如獲取請求和驗證。
你可能使用Xcode的Create NSManagedObjectSubclass功能快速創(chuàng)建單個實體的子類。雖然是個捷徑,但如果你的數(shù)據(jù)模型比較大,它會增加額外的開銷,會在更改模型時帶來許多問題。
重新創(chuàng)建子類意味著清除你所有的自定義模型邏輯--這就意味著你應該在自定義模型以外創(chuàng)建邏輯。它適合于創(chuàng)建帶有托管對象屬性和自定義模型邏輯類別的自定義子類的常規(guī)模式。
命令行工具Mogenerator 會自動化這些精確的任務。每個Core Data實體會生成兩個類。***個類是為機器消耗生成的,當模型改變的時候不斷地被覆蓋。第二個類是為你所有的自定義邏輯生成的,從來不被覆蓋。
Mogenerator 有一系列其他好處,包括以下幾點:
-
讀/寫數(shù)值型屬性時無需使用NSNumber 對象。
-
處理設置的輔助方法
-
創(chuàng)建新實體的輔助方法
-
一個實體識別的方法
Mogenerator可以從Mogenerator website 上可用的DMG文件來安裝,或者通過Homebrew安裝Mogenerator,打開終端,執(zhí)行下面的命令:
- brew install mogenerator
一旦安裝,用cd 命令來改變你應用程序的目錄,然后從終端運行Mogenerator,像這樣:
- $ mogenerator -m MySampleApp/ExampleModel.xcdatamodeld -O MySampleApp/Model --template-var arc=true
在上面的命令中,你可以用-m 選項調(diào)用Mogenerator,緊隨其后的是模型的位置。你也可以用-O 選項指定生成類的位置。當使用ARC時,你需要傳遞--template-var arc=true 選項。
你可以讓Xcode通過創(chuàng)建Run Script Build Phase來運行Mogenerator。Build Phases是編譯期間Xcode必須執(zhí)行的任務的描述。
要添加一個Build Phase,首先選擇target,選擇Build Phases標簽,然后選擇菜單里的Editor / Add Build Phase / Add Run Script Build Phase 。
在新Run Script下的Shell腳本文本區(qū)域里添加以下代碼,確保修改mogenerator 的參數(shù),來適配你的工程:
- if [ "${CONFIGURATION}" == "Debug" ]; then
- echo "Running Mogenerator"
- mogenerator -m MySampleApp/ExampleModel.xcdatamodeld -O MySampleApp/Model --template-var arc=true
- echo "Finished Mogenerator"
- else echo "Skipping Mogenerator"
- fi
為了快速生成子類,現(xiàn)在你已經(jīng)把Mogenerator合并到你的工作流中,你應該好好利用它的其它特性。
比如,不用每次打開原始值,你只要給它們添加Value后綴, 如以下的代碼片段所示:
- // Without Mogenerator
- if ([person.isFriend boolValue]) {
- // Do some work
- }
- // With Mogenerator
- if (person.isFriendValue) {
- // Do some work
- }
因為在Core Data里bool 類型是以NSNumber 類型存儲的,在檢查值是否為真之前你必須調(diào)用person 對象的boolValue 。通過Mogenerator,就不需要額外的步驟了,因為你只要簡單地調(diào)用 isFriendValue即可。
如果Mogenerator對你的工具箱來說看起來像個有用的補充,你可以在Github repository 找到更多有關Mogenerator的信息。
#1. Instruments
Instruments 是OS X 和 iOS里為研究幾乎所有性能和內(nèi)存問題的重要工具--包括Core Data 問題。清單里的其它工具提供了許多自動化和便捷性,但Instruments通常會是研究任何問題或者性能調(diào)整的***站。
下圖是Time Profiler 和Core Data 模板,對Core Data配置來說是最有用的。
默認的Core Data 模板,添加了可選的Faults Instrument功能,通過提供以下功能來幫你調(diào)整和監(jiān)控應用程序的性能:
-
Core Data Fetches Instrument,捕捉獲取次數(shù)和獲取操作的持續(xù)時間。
-
Core Data Cache Misses Instrument,捕捉導致緩存遺漏的故障事件。
-
Core Data Saves Instrument,捕捉托管對象上下文環(huán)境保存事件的信息。
-
Core Data Faults Instrument,捕捉NSManagedObjects 或相關關系的延遲初始化過程發(fā)生的故障事件的信息。
這個是Core Data應用程序中典型的instruments profile。你可以看到獲取請求何時發(fā)生,以及請求所花時間,保存操作何時以及如何發(fā)生,以及何時出現(xiàn)故障燈。
要獲取更多有關Instruments的信息,請下載我們的教程:How to Use Instruments in Xcode。
下一步
Core Data是個很強大的框架,但它會有很多開發(fā)消耗,不過這篇文章中的工具和庫提供了一些方法來高效、有效地幫你解決消耗問題。