在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ iOS/ Core Data 網(wǎng)絡(luò)應(yīng)用實(shí)例
與四軸無人機(jī)的通訊
在沙盒中編寫腳本
結(jié)構(gòu)體和值類型
深入理解 CocoaPods
UICollectionView + UIKit 力學(xué)
NSString 與 Unicode
代碼簽名探析
測試
架構(gòu)
第二期-并發(fā)編程
Metal
自定義控件
iOS 中的行為
行為驅(qū)動開發(fā)
Collection View 動畫
截圖測試
MVVM 介紹
使 Mac 應(yīng)用數(shù)據(jù)腳本化
一個完整的 Core Data 應(yīng)用
插件
字符串
為 iOS 建立 Travis CI
先進(jìn)的自動布局工具箱
動畫
為 iOS 7 重新設(shè)計(jì) App
XPC
從 NSURLConnection 到 NSURLSession
Core Data 網(wǎng)絡(luò)應(yīng)用實(shí)例
GPU 加速下的圖像處理
自定義 Core Data 遷移
子類
與調(diào)試器共舞 - LLDB 的華爾茲
圖片格式
并發(fā)編程:API 及挑戰(zhàn)
IP,TCP 和 HTTP
動畫解釋
響應(yīng)式 Android 應(yīng)用
初識 TextKit
客戶端
View-Layer 協(xié)作
回到 Mac
Android
Core Image 介紹
自定義 Formatters
Scene Kit
調(diào)試
項(xiàng)目介紹
Swift 的強(qiáng)大之處
測試并發(fā)程序
Android 通知中心
調(diào)試:案例學(xué)習(xí)
從 UIKit 到 AppKit
iOS 7 : 隱藏技巧和變通之道
安全
底層并發(fā) API
消息傳遞機(jī)制
更輕量的 View Controllers
用 SQLite 和 FMDB 替代 Core Data
字符串解析
終身學(xué)習(xí)的一代人
視頻
Playground 快速原型制作
Omni 內(nèi)部
同步數(shù)據(jù)
設(shè)計(jì)優(yōu)雅的移動游戲
繪制像素到屏幕上
相機(jī)與照片
音頻 API 一覽
交互式動畫
常見的后臺實(shí)踐
糟糕的測試
避免濫用單例
數(shù)據(jù)模型和模型對象
Core Data
字符串本地化
View Controller 轉(zhuǎn)場
照片框架
響應(yīng)式視圖
Square Register 中的擴(kuò)張
DTrace
基礎(chǔ)集合類
視頻工具箱和硬件加速
字符串渲染
讓東西變得不那么糟
游戲中的多點(diǎn)互聯(lián)
iCloud 和 Core Data
Views
虛擬音域 - 聲音設(shè)計(jì)的藝術(shù)
導(dǎo)航應(yīng)用
線程安全類的設(shè)計(jì)
置換測試: Mock, Stub 和其他
Build 工具
KVC 和 KVO
Core Image 和視頻
Android Intents
在 iOS 上捕獲視頻
四軸無人機(jī)項(xiàng)目
Mach-O 可執(zhí)行文件
UI 測試
值對象
活動追蹤
依賴注入
Swift
項(xiàng)目管理
整潔的 Table View 代碼
Swift 方法的多面性
為什么今天安全仍然重要
Core Data 概述
Foundation
Swift 的函數(shù)式 API
iOS 7 的多任務(wù)
自定義 Collection View 布局
測試 View Controllers
訪談
收據(jù)驗(yàn)證
數(shù)據(jù)同步
自定義 ViewController 容器轉(zhuǎn)場
游戲
調(diào)試核對清單
View Controller 容器
學(xué)無止境
XCTest 測試實(shí)戰(zhàn)
iOS 7
Layer 中自定義屬性的動畫
第一期-更輕量的 View Controllers
精通 iCloud 文檔存儲
代碼審查的藝術(shù):Dropbox 的故事
GPU 加速下的圖像視覺
Artsy
照片擴(kuò)展
理解 Scroll Views
使用 VIPER 構(gòu)建 iOS 應(yīng)用
Android 中的 SQLite 數(shù)據(jù)庫支持
Fetch 請求
導(dǎo)入大數(shù)據(jù)集
iOS 開發(fā)者的 Android 第一課
iOS 上的相機(jī)捕捉
語言標(biāo)簽
同步案例學(xué)習(xí)
依賴注入和注解,為什么 Java 比你想象的要好
編譯器
基于 OpenCV 的人臉識別
玩轉(zhuǎn)字符串
相機(jī)工作原理
Build 過程

Core Data 網(wǎng)絡(luò)應(yīng)用實(shí)例

幾乎每一個應(yīng)用開發(fā)者都需要經(jīng)歷的就是將從 web service 獲取到的數(shù)據(jù)轉(zhuǎn)變到 Core Data 中。這篇文章闡述了如何去做。我們在這里討論的每一個問題在之前的文章中都已經(jīng)描述過了,并且 Apple 在他們的文檔中也提過。然而,從頭到尾回顧一遍對我們來說還是很有益的。

程序所有的代碼都在 GitHub 上。

計(jì)劃

我們將會建立一個簡單、只讀的應(yīng)用程序,用來顯示 CocoaPods 說明的完整列表。這些說明都顯示在 table view 中,所有 pod 的說明都是以分頁的形式,從 web service 取得,并以 JSON 對象返回。

我們這樣來做

  1. 首先,我們創(chuàng)建一個 PodsWebservice 類,用來從 web service 請求所有的說明。
  2. 接著,創(chuàng)建一個 Importer 對象取出說明并將他們導(dǎo)入 Core Data。
  3. 最終,我們展示如何讓最重要的工作在后臺線程中運(yùn)行。

從 Web Service 取得對象

首先,創(chuàng)建一個單獨(dú)的類從 web service 取得數(shù)據(jù)是很不錯的。我們已經(jīng)寫了一個簡單的 web server 示例,用來獲取 CocoaPods 說明并將它們生成 JSON;請求 /specs 這個 URL 會返回一個按字母排序的 pod 說明列表。web service 是分頁的,所以我們需要分開請求每一頁。一個響應(yīng)的示例如下:

{ 
  "number_of_pages": 559,
  "result": [{
    "authors": { "Ash Furrow": "ash@ashfurrow.com" },
    "homepage": "https://github.com/500px/500px-iOS-api",
    "license": "MIT",
    "name": "500px-iOS-api",
  ...

我們想要創(chuàng)建只有一個 fetchAllPods: 方法的類,它有一個回調(diào) block,這將會被每一個頁面調(diào)用。這也可以通過代理實(shí)現(xiàn);但為什么我們選擇用 block,你可以讀一讀這篇有關(guān)消息傳遞機(jī)制的文章。

@interface PodsWebservice : NSObject
- (void)fetchAllPods:(void (^)(NSArray *pods))callback;
@end

這個回調(diào)會被每個頁面調(diào)用。實(shí)現(xiàn)這個方法很簡單。我們創(chuàng)建一個幫助方法,fetchAllPods:page:,它會為一個頁面取得所有的 pods,一旦加載完一頁就讓它再調(diào)用自己。注意一下,為了簡潔,我們這里不考慮處理錯誤,但是你可以在 GitHub 上完整的項(xiàng)目中看到。處理錯誤總是很重要的,至少打印出錯誤,這樣你可以很快檢查到哪些地方?jīng)]有像預(yù)期一樣工作:

- (void)fetchAllPods:(void (^)(NSArray *pods))callback page:(NSUInteger)page
{
    NSString *urlString = [NSString stringWithFormat:@"http://localhost:4567/specs?page=%d", page];
    NSURL *url = [NSURL URLWithString:urlString];
    [[[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:
      ^(NSData *data, NSURLResponse *response, NSError *error) {
        id result = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL];
        if ([result isKindOfClass:[NSDictionary class]]) {
            NSArray *pods = result[@"result"];
            callback(pods);
            NSNumber* numberOfPages = result[@"number_of_pages"];
            NSUInteger nextPage = page + 1;
            if (nextPage < numberOfPages.unsignedIntegerValue) {
                [self fetchAllPods:callback page:nextPage];
            }
        }
    }] resume];
}

要做的就是這些了。我們解析 JSON,做一些非常粗糙的檢查(驗(yàn)證結(jié)果是一個字典),然后調(diào)用回調(diào)函數(shù)。

將對象裝進(jìn) Core Data

現(xiàn)在我們可以將 JSON 裝進(jìn)我們的 Core Data store 中了。為了分清,我們創(chuàng)建一個 Importer 對象來調(diào)用 web service,并且創(chuàng)建或者更新對象。將這些放到一個單獨(dú)的類中很不錯,因?yàn)檫@樣我們的 web service 和 Core Data 部分完全解耦。如果我們想要給 store 提供一個不同的 web service 或者在別的某個地方重用 web service,我們現(xiàn)在并不需要手動處理這兩種情況。同時(shí),不要在 view controller 中編寫邏輯代碼,以后我們可以在別的 app 中更容易復(fù)用這些組件。

我們的 Importer 有兩個方法:

@interface Importer : NSObject
- (id)initWithContext:(NSManagedObjectContext *)context 
           webservice:(PodsWebservice *)webservice;
- (void)import;
@end

通過初始化方法將 context 注入到對象中是一個非常強(qiáng)有力的技巧。當(dāng)編寫測試的時(shí)候,我們可以很容易的注入一個不同的 context。同樣適用于 web service:我們可以很容易的用一個不同的對象模擬 web service。

import 方法負(fù)責(zé)處理邏輯。我們調(diào)用 fetchAllPods: 方法,并且對于每一批 pod 說明,我們都會將它們導(dǎo)入到 context 中。通過將邏輯代碼包裝到 performBlock:,context 會確保所有的事情都在正確的線程中執(zhí)行。然后我們迭代這些說明,并且會為每一個說明生成一個唯一標(biāo)識符(這些標(biāo)識符可以是任何獨(dú)一無二的,只要能確定到唯一一個 model object,正如在 Drew 的文章中解釋那樣。然后我們試著找到 model object,如果不存在則創(chuàng)建一個。loadFromDictionary: 方法需要一個 JSON 字典,并根據(jù)字典中的值更新 model object:

- (void)import
{
    [self.webservice fetchAllPods:^(NSArray *pods)
    {
        [self.context performBlock:^
        {
            for(NSDictionary *podSpec in pods) {
                NSString *identifier = [podSpec[@"name"] stringByAppendingString:podSpec[@"version"]];
                Pod *pod = [Pod findOrCreatePodWithIdentifier:identifier inContext:self.context];
                [pod loadFromDictionary:podSpec];
            }
        }];
    }];
}

上面的代碼中有很多地方要注意。首先,查找或創(chuàng)建方法的效率是非常低下的。在生產(chǎn)環(huán)境的代碼中,你需要批量處理 pods 并且同時(shí)找到他們,正如在《導(dǎo)入大數(shù)據(jù)集》中「高效地導(dǎo)入數(shù)據(jù)」這一節(jié)中所解釋的那樣。

第二,我們直接在 Pod 類(managed object 的子類)中創(chuàng)建 loadFromDictionary:。這意味著我們的 model object 知道 web service。在真實(shí)的代碼中,我們很有可能將這些放到一個類別中,這樣這兩個很完美的分開了。對于這個示例,這無關(guān)要緊。

創(chuàng)建一個獨(dú)立的后臺堆棧

在寫上面的代碼時(shí),我們會先在在主 managed object context 中擁有一切需要的數(shù)據(jù)。我們的應(yīng)用在 table view 控制器中使用一個 fetched results controller 來顯示所有的 pods。當(dāng) managed object context 中的數(shù)據(jù)改變時(shí),fetched results controller 自動更新 data model。然而,在主 managed object context 中處理導(dǎo)入數(shù)據(jù)并不是最優(yōu)的。主線程可能被堵塞,UI 可能沒有反應(yīng)。大多數(shù)時(shí)候,在主線程中處理的工作應(yīng)該是最小限度的,并且造成的延遲應(yīng)當(dāng)難以察覺。如果你的情況正是這樣,那非常好。然而,如果我們想要做些額外的努力,我們可以在后臺線程中處理導(dǎo)入操作。

Apple 在 WWDC 會議以及官方的《Core Data 編程指南》文檔的「Concurrency with Core Data」 一節(jié)中,對于并發(fā)的 Core Data,推薦給開發(fā)者兩種選擇。這兩種都需要獨(dú)立的 managed object contexts,它們要么共享同樣的 persistent store coordinator,要么不共享。在處理很多改變時(shí),擁有獨(dú)立的 persistent store coordinators 提供更出色的性能,因?yàn)閮H需要的鎖只是在 sqlite 級別。擁有共享的 persistent store coordinator 也就意味著擁有共享緩存,當(dāng)你沒有做出很多改變時(shí),這會很快。所以,根據(jù)你的情況而定,你需要衡量哪種方案更好,然后選擇是否需要一個共享的 persistent store coordinator。當(dāng)主 context 是只讀的情況下,根本不需要鎖,因?yàn)?iOS 7 中的 sqlite 有寫前記錄功能并且支持多重讀取和單一寫入。然而,對于我們的示范目的,我們會使用完全獨(dú)立堆棧的處理方式。我們使用下面的代碼設(shè)置一個 managed object context:

- (NSManagedObjectContext *)setupManagedObjectContextWithConcurrencyType:(NSManagedObjectContextConcurrencyType)concurrencyType
{
    NSManagedObjectContext *managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:concurrencyType];
    managedObjectContext.persistentStoreCoordinator =
            [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.managedObjectModel];
    NSError* error;
    [managedObjectContext.persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType 
                                                                  configuration:nil 
                                                                            URL:self.storeURL 
                                                                        options:nil 
                                                                          error:&error];
    if (error) {
        NSLog(@"error: %@", error.localizedDescription);
    }
    return managedObjectContext;
}

然后我們調(diào)用這個方法兩次,一次是為主 managed object context,一次是為后臺 managed object context:

self.managedObjectContext = [self setupManagedObjectContextWithConcurrencyType:NSMainQueueConcurrencyType];
self.backgroundManagedObjectContext = [self setupManagedObjectContextWithConcurrencyType:NSPrivateQueueConcurrencyType];

注意傳遞的參數(shù) NSPrivateQueueConcurrencyType 告訴 Core Data 創(chuàng)建一個獨(dú)立隊(duì)列,這將確保后臺 managed object context 的運(yùn)行發(fā)生在一個獨(dú)立的線程中。

現(xiàn)在就剩一步了:每當(dāng)后臺 context 保存后,我們需要更新主線程。我們在之前第 2 期的這篇文章中描述了如何操作。我們注冊一下,當(dāng) context 保存時(shí)得到一個通知,如果是后臺 context,調(diào)用 mergeChangesFromContextDidSaveNotification: 方法。這就是我們要做的所有事情:

[[NSNotificationCenter defaultCenter]
        addObserverForName:NSManagedObjectContextDidSaveNotification
                    object:nil
                     queue:nil
                usingBlock:^(NSNotification* note) {
    NSManagedObjectContext *moc = self.managedObjectContext;
    if (note.object != moc) {
        [moc performBlock:^(){
            [moc mergeChangesFromContextDidSaveNotification:note];
        }];
    }
 }];

這兒還有一個小忠告:mergeChangesFromContextDidSaveNotification: 是在 performBlock:中發(fā)生的。在我們這個情況下,moc 是主 managed object context,因此,這將會阻塞主線程。

注意你的 UI(即使是只讀的)必須有能力處理對象的改變,或者事件的刪除。Brent Simmons 最近寫了兩篇文章,分別是 《Why Use a Custom Notification for Note Deletion》《Deleting Objects in Core Data》。這些文章解釋說明了如何面對這些情況,如果你在你的 UI 中顯示一個對象,這個對象有可能會發(fā)生改變或者被刪除。

實(shí)現(xiàn)從 UI 進(jìn)行的寫入

你可能覺得上面講的看起來非常簡單,這是因?yàn)閮H有的寫操作是在后臺線程進(jìn)行的。在我們當(dāng)前的應(yīng)用中,我們沒有處理其他方面的合并;并沒有來自主 managed object context 中的改變。為了增加這個,你可以采用不少策略。Drew 的這篇文章很好的闡述了相關(guān)的方法。

根據(jù)你的需求,一個非常簡單的模式或許是這樣:不管用戶何時(shí)改變 UI 中的某些東西,你并不改變 managed object context。相反,你去調(diào)用 web service。如果成功了,你可以從 web service 中得到改變,然后更新你的后臺 context。這些改變隨后回被傳送到主 context。這樣做有兩個弊端:用戶可能需要一段時(shí)間才能看到 UI 的改變,并且如果用戶未聯(lián)網(wǎng),他將不能改變?nèi)魏螙|西。在 Florian 的文章中,描述了我們?nèi)绾问褂貌煌呗宰寫?yīng)用在離線時(shí)也能工作。

如果你正在處理合并,你也需要定義一個合并原則。這又是根據(jù)特定使用情況而定的。如果合并失敗了你可能需要拋出一個錯誤,或者總是給某一個 managed object context 優(yōu)先權(quán)。NSMergePolicy 類描述出了可能的選擇。

結(jié)論

我們已經(jīng)看到如何實(shí)現(xiàn)一個簡單的只讀應(yīng)用,這個應(yīng)用能將從 web service 取得的大量數(shù)據(jù)導(dǎo)入到 Core Data。通過使用后臺 managed object context,我們已經(jīng)建立了一個不會阻塞 UI(除非正在處理合并)的 Core Data 程序。