自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

初識CoreData

移動開發(fā)
這其實是一篇 WWDC 2015 Session 220 的學習筆記,順便整理了下 Core Data 批量操作和聚合操作的小技巧.

這其實是一篇 WWDC 2015 Session 220 的學習筆記,順便整理了下 Core Data 批量操作和聚合操作的小技巧.

批量操作

Core Data 把數(shù)據(jù)庫封裝成了”object graph(對象圖)”,雖然對于面向?qū)ο缶幊虂碚f有了管理 Model 間繼承與關(guān)系的便利性,但同樣也犧牲了性能.比如批量操作時就需要將每條記錄作為NSManagedObject 對象讀取到內(nèi)存中,修改之后再存入數(shù)據(jù)庫.然而用 SQL 語句執(zhí)行既方便又高效.

于是蘋果在 iOS8 發(fā)布時順便弄了個”Batch Updates”,在 iOS9 發(fā)布時又弄了個”Batch Deletions”.這兩個”新技術(shù)”說白了就是直接操作持久層數(shù)據(jù)庫,然后還需要手動更新/刪除內(nèi)存中的 context 好使得我們的 UI 從 context 讀取的內(nèi)容不會出錯.這樣做的好處就是省去了向內(nèi)存的一次寫操作和查找操作,而越過 context 直接操作持久層,最后我們需要自己手動將持久層的變更結(jié)果(BatchResult)重新寫入 context.只有當需要更新/刪除大批量數(shù)據(jù)的時候才需要用到這兩個技術(shù).

然而蘋果至今未提供二者的文檔,關(guān)于”Batch Updates”我在CoreData處理海量數(shù)據(jù)中給出了用法和例子.看了 WWDC2015 Session 220 后覺得 “Batch Deletions” 應該與 “Batch Updates” 用法類似,并且坑爹. PS: 我在 iOS9 上測試 “Batch Updates” 發(fā)現(xiàn)了一個 bug, 每次更新 context 都會漏掉一條記錄,這讓我十分郁悶.

聚合操作

說完了批量操作,再談談聚合操作.在 SQL 語法中有一類聚合函數(shù),比如count(),sum(),max(),min(),avg() 等,它們一般搭配著 group by 甚至 having來使用.然而在號稱”object graph”的 Core Data 中,這種聚合操作在 NSFetchRequest 中也是有替代品的.下面的例子取自CORE DATA AND AGGREGATE FETCHES IN SWIFT:

我們想計算出每條產(chǎn)品線的銷售量和退貨量,可以用下面的 SQL 語句搞定:

  1. SELECT ProductLine, SUM(Sold) as SoldCount, SUM(Returned) as ReturnedCount FROM Products GROUP BY ProductLine 

NSFetchRequest 有個 propertiesToGroupBy 屬性,正好對應著 group by 語句:

  1. // Build out our fetch request the usual way 
  2. let request = NSFetchRequest(entityName: self.entityName) 
  3. // This is the column we are grouping by. Notice this is the only non aggregate column. 
  4. request.propertiesToGroupBy = ["productLine"

下面還需要映射 SQL 語句中聚合函數(shù)及其計算后的結(jié)果,此時我們需要用到NSExpressionDescription 和 NSExpression 來替換 SQL 中的ProductLine, SUM(Sold) as SoldCount, SUM(Returned) as ReturnedCount:

  1. // Create an array of AnyObject since it needs to contain multiple types--strings and 
  2. // NSExpressionDescriptions 
  3. var expressionDescriptions = [AnyObject]() 
  4.      
  5. // We want productLine to be one of the columns returned, so just add it as a string 
  6. expressionDescriptions.append("productLine"
  7.      
  8.  
  9.  
  10. // Create an expression description for our SoldCount column 
  11. var expressionDescription = NSExpressionDescription() 
  12. // Name the column 
  13. expressionDescription.name = "SoldCount" 
  14. // Use an expression to specify what aggregate action we want to take and 
  15. // on which column. In this case sum on the sold column 
  16. expressionDescription.expression = NSExpression(format: "@sum.sold"
  17. // Specify the return type we expect 
  18. expressionDescription.expressionResultType = .Integer32AttributeType 
  19. // Append the description to our array 
  20. expressionDescriptions.append(expressionDescription) 
  21.  
  22.  
  23.      
  24. // Create an expression description for our ReturnedCount column 
  25. expressionDescription = NSExpressionDescription() 
  26. // Name the column 
  27. expressionDescription.name = "ReturnedCount" 
  28. // Use an expression to specify what aggregate action we want to take and 
  29. // on which column. In this case sum on the returned column 
  30. expressionDescription.expression = NSExpression(format: "@sum.returned"
  31. // Specify the return type we expect 
  32. expressionDescription.expressionResultType = .Integer32AttributeType 
  33. // Append the description to our array 
  34. expressionDescriptions.append(expressionDescription) 

NSExpressionDescription 是用于表示那些抓取結(jié)果中實體中不存在的列名,比如我們這次用的聚合函數(shù)所計算的結(jié)果并不能在實體中找到對應的列,于是我們就得給它起個新名字,這就相當于 SQL 中的 as,這里對應著 NSExpressionDescription 的 name 屬性.而聚合函數(shù)表達式就需要用 NSExpression 對象來表示,比如 NSExpression(format: "@sum.returned")就是對”returned”這列求和.

像本例中這樣初始化 NSExpression 需要對格式化語法較為熟悉(比如"@sum.returned"),初學者建議看看官方的例子,使用容易理解的構(gòu)造方法一步步拼湊成想要的結(jié)果:Core Data Programming Guide

將以上這三個”列描述”依次添加到 expressionDescriptions 數(shù)組中,最后要賦值給NSFetchRequest 的 propertiesToFetch 屬性:

  1. // Hand off our expression descriptions to the propertiesToFetch field. Expressed as strings 
  2. // these are ["productLine", "SoldCount", "ReturnedCount"] where productLine is the value 
  3. // we are grouping by. 
  4. request.propertiesToFetch = expressionDescriptions 

propertiesToFetch 屬性其實是個 NSPropertyDescription 類型數(shù)組,能表示屬性,一對一關(guān)系和表達式.既然是個大雜燴,NSPropertyDescription 也就有一些子類:NSAttributeDescription,NSExpressionDescription,NSFetchedPropertyDescription,NSRelationshipDescription.我們這里用到的便是 NSExpressionDescription.

在設(shè)定 propertiesToFetch 屬性之前必需要設(shè)定好 NSFetchRequest 的 entity 屬性,否則會拋出 NSInvalidArgumentException 類型的異常.并且只有當 resultType 類型設(shè)為NSDictionaryResultType 時才生效:

  1. // Specify we want dictionaries to be returned 
  2. request.resultType = .DictionaryResultType 

最終結(jié)果:

  1.     ["SoldCount"48"productLine": Bowler, "ReturnedCount"4],  
  2.     ["SoldCount"142"productLine": Stetson, "ReturnedCount"27],  
  3.     ["SoldCount"50"productLine": Top Hat, "ReturnedCount"6

WWDC2015 Core Data 的一些新特性

蘋果號稱有超過40萬個 APP 使用 Core Data,并能讓開發(fā)者少寫50%~70%的代碼.并在內(nèi)存性能上強調(diào)卓越的內(nèi)存拓展和主動式惰性加載,炫耀了它跟 UI 良好的綁定機制,還提供了幾種多重寫入的合并策略.然而這不能阻止開發(fā)者對 Core Data 的吐槽,畢竟建立于持久層之上的”object graph”還做不到像 SQL 那樣面面俱到,于是今年針對 Core Data 新增的 API 更像是查缺補漏,并沒有帶來重大功能更新.

NSManagedObject 新增 API

hasPersistentChangedValues

  1. var hasPersistentChangedValues: Bool { get } 

用此屬性可確定 NSManagedObject 的值與 “persistent store” 是否相同.

objectIDsForRelationshipNamed

  1. func objectIDsForRelationshipNamed(_ key: String) -> [NSManagedObjectID] 

適用于大量的多對多關(guān)系.由于我們不想將整個關(guān)系網(wǎng)絡加載到內(nèi)存中,所以這個方法僅返回相關(guān)聯(lián)的 ID.下面是一個例子:

  1. let relations = person.objectIDsForRelationshipNamed("family"
  2.  
  3. let fetchFamily = NSFetchRequest(entityName:"Person"
  4. fetchFamily.fetchBatchSize = 100 
  5. fetchFamily.predicate = NSPredicate(format: "self IN %@", relations) 
  6.  
  7. let batchedRelations = managedObjectContext.executeFetchRequest(fetchFamily) 
  8.  
  9. for relative in batchedRelations { 
  10.     //work with relations 100 rows at a time 

通過給出的關(guān)系名稱 “family” 來獲取對應的 ID, 并每次遍歷100行記錄,實現(xiàn)了內(nèi)存占用的可控性.

#p#

NSManagedObjectContext 新增 API

refreshAllObjects

  1. func refreshAllObjects() 

正如其名字所描述的那樣,它的功能就是刷新 context 中所有對象,但會保留未保存的變更.相比reset 方法不同的是它會依然保留 NSManagedObject 對象的有效性,我們無需重新抓取任何對象.正因如此,它很適用于打破一些因遍歷雙向關(guān)系循環(huán)而產(chǎn)生的保留環(huán).

mergeChangesFromRemoteContextSave

  1. class func mergeChangesFromRemoteContextSave(_ changeNotificationData: [NSObject : AnyObject], intoContexts contexts: [NSManagedObjectContext]) 

在 store 中使用多個 coordinator 時,這個方法將會從一個 coordinator 接受一個通知,并將其應用到另一個 coordinator 中的 context 上.這使得我們可以在所有 context 中存有最新的數(shù)據(jù),Core Data 會維護好所有的 context.

shouldDeleteInaccessibleFaults

  1. var shouldDeleteInaccessibleFaults: Bool 

Core Data 偶爾會拋異常,但Core Data 不能加載故障, 因為它的主動式惰性加載對象使得內(nèi)存中只保留對象圖中的一部分.所以很有可能當我遍歷關(guān)系時要試圖回到磁盤上查找,但此時對象早已被刪除了.于是 shouldDeleteInaccessibleFaults 屬性應運而生,默認值為 YES.

如果我們在某處遇到了故障,我們會將其標記為已刪除.任何丟失的屬性將會被設(shè)為null,nil或0.這就使得我們的 app 繼續(xù)運行,并認為發(fā)生故障的對象已被刪除.這樣程序就不會再崩潰.

NSPersistentStoreCoordinator 新增 API

增加這兩個新的 API 的原因是很多開發(fā)者繞過 Core Data 的 API 來直接操作底層數(shù)據(jù)庫文件.因為NSFileManager 和 POSIX 對數(shù)據(jù)庫都不友好,并且如果此時文件的 open 連接沒關(guān)閉的話會損壞文件.

destroyPersistentStoreAtURL

  1. func destroyPersistentStoreAtURL(_ url: NSURL, withType storeType: String, options options: [NSObject : AnyObject]?) throws 

傳入的選項與 addPersistentStoreWithType 方法要一樣,刪除對應類型的 persistent store.

replacePersistentStoreAtURL

  1. func replacePersistentStoreAtURL(_ destinationURL: NSURL, destinationOptions destinationOptions: [NSObject : AnyObject]?, withPersistentStoreFromURL sourceURL: NSURL, sourceOptions sourceOptions: [NSObject : AnyObject]?, storeType storeType: String) throws 

與上面的 destroy 一個套路,就是 replace 而已.如果目標位置不存在數(shù)據(jù)庫,那么這個 replace 就相當于拷貝操作了.

Unique Constraints

很多時候我們在創(chuàng)建一個對象之前會查看它是否已經(jīng)存在,如果存在的話就會更新它,否則就創(chuàng)建對象.這很可能產(chǎn)生一個競態(tài)條件,如果多線程同時執(zhí)行下面這段代碼, 很可能就創(chuàng)建了多個重復的對象:

  1. managedObjectContext.performBlock { 
  2.     let createRequest = NSFetchRequest(entityName: "Recipe"
  3.     createRequest.resultType = ManagedObjectIDResultType 
  4.     let predicate = NSPredicate(format: "source = %@", source) 
  5.     let results = managedObjectContext.executeFetchRequest(createRequest) 
  6.     if (results.count) { 
  7.         //update it! 
  8.     } else { 
  9.         //create it! 
  10.     } 

現(xiàn)在 Core Data 可以搞定這個事情了.我們設(shè)定屬性的值唯一,類似于 SQL 中的 unique 約束.諸如電子郵件,電話號, ISBN 等場景都適用此.同時別忘了 Core Data 的對象圖中實體的繼承關(guān)系,這里規(guī)定子類會從父類繼承到具有 Unique 約束的屬性,并可以將更多的屬性設(shè)為 Unique.

為實體設(shè)置 Unique 屬性十分簡單,只需要在 Xcode 中選中對應的實體,打開 “Data Model inspector” 就可以看到 “Constraints”, 點擊加號添加就好:

Model Caching

這是個輕量級的數(shù)據(jù)版本自動遷移解決方案.它會緩存舊版本數(shù)據(jù)中已創(chuàng)建的NSManagedObject 對象會被緩存到 store 中,并被遷移到合適的 store 中.

Generated Subclasses

在 Xcode7 中,自動創(chuàng)建 NSManagedObject 子類時將不再在對應實體子類文件中自動填充模板代碼,而是同時創(chuàng)建Category(Objective-C文件) 或 extension(Swift文件),并將模板代碼自動填寫進去.這樣帶來的好處是將我們自己寫的代碼跟 Xcode 生成的模板代碼分開,更易于更新維護.

責任編輯:倪明 來源: 玉令天下的博客
相關(guān)推薦

2011-07-08 17:57:37

iPhone CoreData 數(shù)據(jù)庫

2015-03-12 09:51:09

CoreDataiCloud

2015-06-11 15:25:43

ASP.NET

2014-07-24 09:22:37

iOS 8OS XCoreData

2010-07-12 17:02:33

Netstat TCP

2010-07-08 12:34:46

HART協(xié)議

2010-09-09 17:05:37

PPPoE協(xié)議

2012-03-05 13:41:58

OpenFlow

2010-07-25 14:37:40

telnet命令

2010-09-10 14:25:00

Daytime協(xié)議

2011-06-14 14:41:14

Python Qt

2010-08-31 16:03:15

2011-06-14 15:45:02

Qt Object

2010-09-17 15:28:45

Internet網(wǎng)絡協(xié)

2011-07-01 16:04:45

Qt Python

2011-06-30 11:14:23

QML Desktop

2023-11-08 14:41:12

Python函數(shù)

2010-06-17 14:57:44

RS-232協(xié)議

2011-06-14 17:03:03

QML Qt

2022-08-05 11:33:40

云計算
點贊
收藏

51CTO技術(shù)棧公眾號