<簡書 — 劉小壯> http://www.jianshu.com/p/a4710356244dandroid
以前兩篇文章都比較偏理論,文字表達比較多一些,但都是乾貨!學習時先理解理論知識,才能更好的幫助後面的理解。在這篇文章中,將會涉及關於
CoreData
的一些複雜操做,這些操做會涉及分頁查詢、模糊查詢、批處理等高級操做。ios經過這些操做能夠更好的使用
CoreData
,提高CoreData
性能。文章中將會出現大量示例代碼,經過代碼的方式更有助於理解。
文章內容還會比較多,但願各位耐心看完。git文章中若有疏漏或錯誤,還請各位及時提出,謝謝!😊github
在iOS
開發過程當中,不少需求都須要用到過濾條件。例如過濾一個集合對象中存儲的對象,能夠經過Foundation
框架下的NSPredicate
類來執行這個操做。正則表達式
CoreData
中能夠經過設置NSFetchRequest
類的predicate
屬性,來設置一個NSPredicate
類型的謂詞對象當作過濾條件。經過設置這個過濾條件,能夠只獲取符合過濾條件的託管對象,不會將全部託管對象都加載到內存中。這樣是很是節省內存和加快查找速度的,設計一個好的NSPredicate
能夠優化CoreData
搜索性能。數據庫
NSPredicate
更加偏向於天然語言,不像SQLite
同樣有不少固定的語法,看起來也更加清晰易懂。例以下面須要查找條件爲年齡30歲以上,而且包括30歲的條件。express
[NSPredicate predicateWithFormat:@"age >= 30"]
能夠經過NSPredicate
對iOS
中的集合對象執行過濾操做,能夠是NSArray
、NSSet
及其子類。數組
對不可變數組NSArray
執行的過濾,過濾後會返回一個NSArray
類型的結果數組,其中存儲着符合過濾條件的對象。緩存
NSArray *results = [array filteredArrayUsingPredicate:predicate]
對可變數組NSMutableArray
執行的過濾條件,過濾後會直接改變原集合對象內部存儲的對象,刪除不符合條件的對象。安全
[arrayM filterUsingPredicate:predicate]
謂詞不僅能夠過濾簡單條件,還能夠過濾複雜條件,設置複合過濾條件。
[NSPredicate predicateWithFormat:@"(age < 25) AND (firstName = XiaoZhuang)"]
固然也能夠經過NSCompoundPredicate
對象來設置複合過濾條件,返回結果是一個NSPredicate
的子類NSCompoundPredicate
對象。
[[NSCompoundPredicate alloc] initWithType:NSAndPredicateType subpredicates:@[predicate1, predicate2]]
枚舉值NSCompoundPredicateType
參數,能夠設置三種複合條件,枚舉值很是直觀很容易看懂。
下面是列舉的一些NSPredicate
的基礎語法,這些語法看起來很是容易理解,更復雜的用法能夠去看蘋果的官方API。
語法 | 做用 |
---|---|
== | 判斷是否相等 |
>= | 大於或等於 |
<= | 小於或等於 |
> | 大於 |
< | 小於 |
!= | 不等於 |
AND 或 && | 和 |
OR 或 II | 或 |
NOT 或 ! | 非 |
NSPredicate
中還可使用正則表達式,能夠經過正則表達式完成一些複雜需求,這使得謂詞的功能更增強大,例以下面是一個手機號驗證的正則表達式。
NSString *mobile = @"^1(3[0-9]|5[0-35-9]|8[025-9])\\d{8}$"; NSPredicate *regexmobile = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", mobile];
NSPredicate
支持對數據的模糊查詢,例以下面使用通配符來匹配包含lxz的結果,具體CoreData
中的使用在下面會講到。
[NSPredicate predicateWithFormat:@"name LIKE %@", @"*lxz*"]
NSPredicate
在建立查詢條件時,還支持設置被匹配目標的keyPath
,也就是設置更深層被匹配的目標。例以下面設置employee
的name
屬性爲查找條件,就是用點語法設置的keyPath
。
[NSPredicate predicateWithFormat:@"employee.name = %@", @"lxz"]
在以前的文章中,執行下面MOC
的fetchRequest
方法,通常都須要傳入一個NSFetchRequest
類型的參數。這個request
參數能夠作一些設置操做,這樣就能夠以較優的性能獲取指定的數據。
- (nullable NSArray *)executeFetchRequest:(NSFetchRequest *)request error:(NSError **)error;
在執行fetch
操做前,能夠給NSFetchRequest
設置一些參數,這些參數包括謂詞、排序等條件,下面是一些基礎的設置。
fetchRequestWithEntityName:
或初始化方法來指定表名。NSPredicate
類型的屬性,能夠設置查找條件,這個屬性在開發中用得最多。NSPredicate
能夠包括固定格式的條件以及正則表達式。sortDescriptors
屬性,能夠設置獲取結果數組的排序方式,這個屬性是一個數組類型,也就是能夠設置多種排序條件。(可是注意條件不要衝突)fetchOffset
屬性設置從查詢結果的第幾個開始獲取,經過fetchLimit
屬性設置每次獲取多少個。主要用於分頁查詢,後面會講。MOC
執行fetch
操做後,獲取的結果是以數組的形式存儲的,數組中存儲的就是託管對象。NSFetchRequest
提供了參數resultType
,參數類型是一個枚舉類型。經過這個參數,能夠設置執行fetch
操做後返回的數據類型。
NSManagedObject
的子類,也就是託管對象,這是默認選項。NSManagedObjectID
類型的對象,也就是NSManagedObject
的ID
,對內存佔用比較小。MOC
能夠經過NSManagedObjectID
對象獲取對應的託管對象,而且能夠經過緩存NSManagedObjectID
參數來節省內存消耗。count
值,這個操做是發生在數據庫層級的,並不須要將數據加載到內存中。// 創建獲取數據的請求對象,並指明操做Employee表 NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Employee"]; // 設置請求條件,經過設置的條件,來過濾出須要的數據 NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name = %@", @"lxz"]; request.predicate = predicate; // 設置請求結果排序方式,能夠設置一個或一組排序方式,最後將全部的排序方式添加到排序數組中 NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:@"height" ascending:YES]; // NSSortDescriptor的操做都是在SQLite層級完成的,不會將對象加載到內存中,因此對內存的消耗是很是小的 request.sortDescriptors = @[sort]; // 執行獲取請求操做,獲取的託管對象將會被存儲在一個數組中並返回 NSError *error = nil; NSArray<Employee *> *employees = [context executeFetchRequest:request error:&error]; [employees enumerateObjectsUsingBlock:^(Employee * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { NSLog(@"Employee Name : %@, Height : %@, Brithday : %@", obj.name, obj.height, obj.brithday); }]; // 錯誤處理 if (error) { NSLog(@"CoreData Fetch Data Error : %@", error); }
這裏設置NSFetchRequest
對象的一些請求條件,設置查找Employee
表中name
爲lxz
的數據,而且將全部符合的數據用height
值升序的方式排列。
一個模型文件中的不一樣實體間,能夠設置實體間的關聯關係,這個在以前的文章中講過。實體關聯關係分爲對一或對多,也能夠設置是否雙向關聯。
這裏演示的實體只是簡單的To One
的關係,而且下面會給出設置是否雙向關聯的區別對比。
// 建立託管對象,並將其關聯到指定的MOC上 Employee *zsEmployee = [NSEntityDescription insertNewObjectForEntityForName:@"Employee" inManagedObjectContext:context]; zsEmployee.name = @"zhangsan"; zsEmployee.height = @1.9f; zsEmployee.brithday = [NSDate date]; Employee *lsEmployee = [NSEntityDescription insertNewObjectForEntityForName:@"Employee" inManagedObjectContext:context]; lsEmployee.name = @"lisi"; lsEmployee.height = @1.7f; lsEmployee.brithday = [NSDate date]; Department *iosDepartment = [NSEntityDescription insertNewObjectForEntityForName:@"Department" inManagedObjectContext:context]; iosDepartment.departName = @"iOS"; iosDepartment.createDate = [NSDate date]; iosDepartment.employee = zsEmployee; Department *androidDepartment = [NSEntityDescription insertNewObjectForEntityForName:@"Department" inManagedObjectContext:context]; androidDepartment.departName = @"android"; androidDepartment.createDate = [NSDate date]; androidDepartment.employee = lsEmployee; // 執行存儲操做 NSError *error = nil; if (context.hasChanges) { [context save:&error]; } // 錯誤處理 if (error) { NSLog(@"Association Table Add Data Error : %@", error); }
上面建立了四個實體,而且將Employee
都關聯到Department
上,完成關聯操做後經過MOC
存儲到本地。
能夠看到上面全部的託管對象建立時,都使用NSEntityDescription
的insert
方法建立,並和上下文創建關係。這時就想問了,我能直接採用傳統的init
方法建立嗎?
會崩的😱!建立託管對象時須要指定MOC,在運行時動態的生成set
、get
方法。可是直接經過init
方法初始化的對象,系統是不知道這裏是須要系統自身生成set
、get
方法的,並且系統也不知道應該對應哪一個MOC
,會致使方法未實現的崩潰。因此就出現了開發中常常出現的錯誤,以下面崩潰信息:
-[Employee setName:]: unrecognized selector sent to instance 0x7fa665900f60
在上一篇文章中提到過雙向關聯的概念,也就是設置Relationship
時Inverse
是否爲空。下面是Employee
和Department
在數據庫中,設置inverse
和沒有設置inverse
的兩種數據存儲,能夠很清晰的對比出設置雙向關聯的區別。
測試代碼仍是用上面插入實體的代碼,只是更改inverse
選項。
從圖中能夠看出,未設置雙向關聯的實體,Department
關聯Employee
爲屬性並存儲後,Department
表中的關係是存在的,但Employee
表中的關係依然是空的。而設置雙向關聯後的實體,在Department
關聯Employee
爲屬性並存儲後,Employee
在表中自動設置了和Department
的關係。
雙向關聯的關係不僅體如今數據庫中,在程序運行過程當中託管對象的關聯屬性,也是隨着發生變化的。雙向關聯的雙方,一方的關聯屬性設置關係後,另外一方關聯屬性的關係也會發生變化。用下面的代碼打印一下各自的關聯屬性,結果和上面數據庫的變化是同樣的。
NSLog(@"Department : %@, Employee : %@", androidDepartment.employee, lsEmployee.department);
// 建立獲取數據的請求對象,並指明操做Department表 NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Department"]; // 設置請求條件,設置employee的name爲請求條件。NSPredicate的好處在於,能夠設置keyPath條件 NSPredicate *predicate = [NSPredicate predicateWithFormat:@"employee.name = %@", @"lxz"]; request.predicate = predicate; // 執行查找操做 NSError *error = nil; NSArray<Department *> *departments = [context executeFetchRequest:request error:&error]; [departments enumerateObjectsUsingBlock:^(Department * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { NSLog(@"Department Search Result DepartName : %@, employee name : %@", obj.departName, obj.employee.name); }]; // 錯誤處理 if (error) { NSLog(@"Department Search Error : %@", error); }
查找Department
實體,並打印實體內容。就像上面講的雙向關係同樣,有關聯關係的實體,本身被查找出來後,也會將與之關聯的其餘實體也查找出來,而且查找出來的實體都是關聯着MOC
的。
在從本地存儲區獲取數據時,能夠指定從第幾個獲取,以及本次查詢獲取多少個數據,聯合起來使用就是分頁查詢。固然也能夠根據需求,單獨使用這兩個API
。
這種需求在實際開發中很是常見,例如TableView
中,上拉加載數據,每次加載20條數據,就能夠利用分頁查詢輕鬆實現。
// 建立獲取數據的請求對象,並指明操做Employee表 NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Employee"]; // 設置查找起始點,這裏是從搜索結果的第六個開始獲取 request.fetchOffset = 6; // 設置分頁,每次請求獲取六個託管對象 request.fetchLimit = 6; // 設置排序規則,這裏設置身高升序排序 NSSortDescriptor *descriptor = [NSSortDescriptor sortDescriptorWithKey:@"height" ascending:YES]; request.sortDescriptors = @[descriptor]; // 執行查詢操做 NSError *error = nil; NSArray<Employee *> *employees = [context executeFetchRequest:request error:&error]; [employees enumerateObjectsUsingBlock:^(Employee * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { NSLog(@"Page Search Result Name : %@, height : %@", obj.name, obj.height); }]; // 錯誤處理 if (error) { NSLog(@"Page Search Data Error : %@", error); }
上面是一個按照身高升序排序,分頁獲取搜索結果的例子。查找Employee
表中的實體,將結果按照height
字段升序排序,並從結果的第六個開始查找,而且設置獲取的數量也是六個。
有時須要獲取具備某些相同特徵的數據,這樣就須要對查詢的結果作模糊匹配。在CoreData
執行模糊匹配時,能夠經過NSPredicate
執行這個操做。
// 建立獲取數據的請求對象,設置對Employee表進行操做 NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Employee"]; // 建立模糊查詢條件。這裏設置的帶通配符的查詢,查詢條件是結果包含lxz NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name LIKE %@", @"*lxz*"]; request.predicate = predicate; // 執行查詢操做 NSError *error = nil; NSArray<Employee *> *employees = [context executeFetchRequest:request error:&error]; [employees enumerateObjectsUsingBlock:^(Employee * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { NSLog(@"Fuzzy Search Result Name : %@, height : %@", obj.name, obj.height); }]; // 錯誤處理 if (error) { NSLog(@"Fuzzy Search Data Error : %@", error); }
上面是使用通配符的方式進行模糊查詢,NSPredicate
支持多種形式的模糊查詢,下面列舉一些簡單的匹配方式。模糊查詢條件對大小寫不敏感,因此查詢條件大小寫都可。
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name BEGINSWITH %@", @"lxz"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name ENDSWITH %@", @"lxz"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name contains %@", @"lxz"];
NSPredicate predicate = [NSPredicate predicateWithFormat:@"name LIKE %@", @"lxz*"];
在以前的文章中談到在模型文件中設置請求模板,也就是在.xcdatamodeld
文件中,設置Fetch Requests
,使用時能夠經過對應的NSManagedObjectModel
獲取設置好的模板。
.... 省略上下文建立步驟 .... // 經過MOC獲取模型文件對應的託管對象模型 NSManagedObjectModel *model = context.persistentStoreCoordinator.managedObjectModel; // 經過.xcdatamodeld文件中設置的模板名,獲取請求對象 NSFetchRequest *fetchRequest = [model fetchRequestTemplateForName:@"EmployeeFR"]; // 請求數據,下面的操做和普通請求同樣 NSError *error = nil; NSArray<Employee *> *dataList = [context executeFetchRequest:fetchRequest error:&error]; [dataList enumerateObjectsUsingBlock:^(Employee * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { NSLog(@"Employee.count = %ld, Employee.height = %f", dataList.count, [obj.height floatValue]); }]; // 錯誤處理 if (error) { NSLog(@"Execute Fetch Request Error : %@", error); }
開發過程當中有時須要只獲取所需數據的Count
值,也就是執行獲取操做後數組中所存儲的對象數量。遇到這個需求,若是像以前同樣MOC
執行獲取操做,獲取到數組而後取Count
,這樣對內存消耗是很大的。
對於這個需求,蘋果提供了兩種經常使用的方式獲取這個Count
值。這兩種獲取操做,都是在數據庫中完成的,並不須要將託管對象加載到內存中,對內存的開銷也是很小的。
// 設置過濾條件,能夠根據需求設置本身的過濾條件 NSPredicate *predicate = [NSPredicate predicateWithFormat:@"height < 2"]; // 建立請求對象,並指明操做Employee表 NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Employee"]; fetchRequest.predicate = predicate; // 這一步是關鍵。設置返回結果類型爲Count,返回結果爲NSNumber類型 fetchRequest.resultType = NSCountResultType; // 執行查詢操做,返回的結果仍是數組,數組中只存在一個對象,就是計算出的Count值 NSError *error = nil; NSArray *dataList = [context executeFetchRequest:fetchRequest error:&error]; NSInteger count = [dataList.firstObject integerValue]; NSLog(@"fetch request result Employee.count = %ld", count); // 錯誤處理 if (error) { NSLog(@"fetch request result error : %@", error); }
方法1中設置NSFetchRequest
對象的resultType
爲NSCountResultType
,獲取到結果的Count
值。這個枚舉值在以前的文章中提到過,除了Count
參數,還能夠設置其餘三種參數。
// 設置過濾條件 NSPredicate *predicate = [NSPredicate predicateWithFormat:@"height < 2"]; // 建立請求對象,指明操做Employee表 NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Employee"]; fetchRequest.predicate = predicate; // 經過調用MOC的countForFetchRequest:error:方法,獲取請求結果count值,返回結果直接是NSUInteger類型變量 NSError *error = nil; NSUInteger count = [context countForFetchRequest:fetchRequest error:&error]; NSLog(@"fetch request result count is : %ld", count); // 錯誤處理 if (error) { NSLog(@"fetch request result error : %@", error); }
MOC
提供了專門獲取請求結果Count
值的方法,經過這個方法能夠直接返回一個NSUInteger
類型的Count
值,使用起來比上面的方法更方便點,其餘都是同樣的。
假設有需求是對Employee
表中,全部託管對象的height
屬性計算總和。這個需求在數據量比較大的狀況下,將全部託管對象加載到內存中是很是消耗內存的,就算批量加載也比較耗時耗內存。
CoreData
對於這樣的需求,提供了位運算的功能。MOC
在執行請求時,是支持對數據進行位運算的。這個操做依然是在數據庫層完成的,對內存的佔用很是小。
// 建立請求對象,指明操做Employee表 NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Employee"]; // 設置返回值爲字典類型,這是爲告終果能夠經過設置的name名取出,這一步是必須的 fetchRequest.resultType = NSDictionaryResultType; // 建立描述對象 NSExpressionDescription *expressionDes = [[NSExpressionDescription alloc] init]; // 設置描述對象的name,最後結果須要用這個name當作key來取出結果 expressionDes.name = @"sumOperatin"; // 設置返回值類型,根據運算結果設置類型 expressionDes.expressionResultType = NSFloatAttributeType; // 建立具體描述對象,用來描述對那個屬性進行什麼運算(可執行的運算類型不少,這裏描述的是對height屬性,作sum運算) NSExpression *expression = [NSExpression expressionForFunction:@"sum:" arguments:@[[NSExpression expressionForKeyPath:@"height"]]]; // 只能對應一個具體描述對象 expressionDes.expression = expression; // 給請求對象設置描述對象,這裏是一個數組類型,也就是能夠設置多個描述對象 fetchRequest.propertiesToFetch = @[expressionDes]; // 執行請求,返回值仍是一個數組,數組中只有一個元素,就是存儲計算結果的字典 NSError *error = nil; NSArray *resultArr = [context executeFetchRequest:fetchRequest error:&error]; // 經過上面設置的name值,當作請求結果的key取出計算結果 NSNumber *number = resultArr.firstObject[@"sumOperatin"]; NSLog(@"fetch request result is %f", [number floatValue]); // 錯誤處理 if (error) { NSLog(@"fetch request result error : %@", error); }
從執行結果能夠看到,MOC
對全部查找到的託管對象height
屬性執行了求和操做,並將結果放在字典中返回。位運算主要是經過NSFetchRequest
對象的propertiesToFetch
屬性設置,這個屬性能夠設置多個描述對象,最後經過不一樣的name
當作key
來取出結果便可。
NSExpression
類能夠描述多種運算,能夠在NSExpression.h
文件中的註釋部分,看到全部支持的運算類型,大概看了一下有二十多種運算。並且除了上面NSExpression
調用的方法,此類還支持點語法的位運算,例以下面的例子。
[NSExpression expressionWithFormat:@"@sum.height"];
在使用CoreData
以前,我和公司同事也討論過,假設遇到須要大量數據處理的時候怎麼辦。CoreData
對於大量數據處理的靈活性確定不如SQLite
,這時候還須要本身使用其餘方式優化數據處理。雖然在移動端這種狀況不多出現,可是在持久層設計時仍是要考慮這方面。
當須要進行數據的處理時,CoreData
須要先將數據加載到內存中,而後才能對數據進行處理。這樣對於大量數據來講,都加載到內存中是很是消耗內存的,並且容易致使崩潰的發生。若是遇到更改全部數據的某個字段這樣的簡單需求,須要將相關的託管對象都加載到內存中,而後進行更改、保存。
對於上面這樣的問題,CoreData
在iOS8
推出了批量更新API,經過這個API
能夠直接在數據庫一層就完成更新操做,而不須要將數據加載到內存。除了批量更新操做,在iOS9
中還推出了批量刪除API,也是在數據庫一層完成的操做。關於批處理的API
不少都是iOS8
、iOS9
出來的,使用時須要注意版本兼容。
可是有個問題,批量更新和批量刪除的兩個API
,都是直接對數據庫進行操做,更新完以後會致使MOC
緩存和本地持久化數據不一樣步的問題。因此須要手動刷新受影響的MOC中存儲的託管對象,使MOC
和本地統一。假設你使用了NSFetchedResultsController
,爲了保證界面和數據的統一,這一步更新操做更須要作。
// 建立批量更新對象,並指明操做Employee表。 NSBatchUpdateRequest *updateRequest = [NSBatchUpdateRequest batchUpdateRequestWithEntityName:@"Employee"]; // 設置返回值類型,默認是什麼都不返回(NSStatusOnlyResultType),這裏設置返回發生改變的對象Count值 updateRequest.resultType = NSUpdatedObjectsCountResultType; // 設置發生改變字段的字典 updateRequest.propertiesToUpdate = @{@"height" : [NSNumber numberWithFloat:5.f]}; // 執行請求後,返回值是一個特定的result對象,經過result的屬性獲取返回的結果。MOC的這個API是從iOS8出來的,因此須要注意版本兼容。 NSError *error = nil; NSBatchUpdateResult *result = [context executeRequest:updateRequest error:&error]; NSLog(@"batch update count is %ld", [result.result integerValue]); // 錯誤處理 if (error) { NSLog(@"batch update request result error : %@", error); } // 更新MOC中的託管對象,使MOC和本地持久化區數據同步 [context refreshAllObjects];
上面對Employee
表中全部的託管對象height
值作了批量更新,在更新時經過設置propertiesToUpdate
字典來控制更新字段和更新的值,設置格式是字段名 : 新值
。經過設置批處理對象的predicate
屬性,設置一個謂詞對象來控制受影響的對象。
還能夠對多個存儲區(數據庫)作一樣批處理操做,經過設置其父類的affectedStores
屬性,類型是一個數組,能夠包含受影響的存儲區,多個存儲區的操做對批量刪除一樣適用。
MOC
在執行請求方法時,發現方法名也不同了,執行的是executeRequest: error:
方法,這個方法是從iOS8
以後出來的。方法傳入的參數是NSBatchUpdateRequest
類,此類並非繼承自NSFetchRequest
類,而是直接繼承自NSPersistentStoreRequest
,和NSFetchRequest
是平級關係。
// 建立請求對象,並指明對Employee表作操做 NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Employee"]; // 經過謂詞設置過濾條件,設置條件爲height小於1.7 NSPredicate *predicate = [NSPredicate predicateWithFormat:@"height < %f", 1.7f]; fetchRequest.predicate = predicate; // 建立批量刪除請求,並使用上面建立的請求對象當作參數進行初始化 NSBatchDeleteRequest *deleteRequest = [[NSBatchDeleteRequest alloc] initWithFetchRequest:fetchRequest]; // 設置請求結果類型,設置爲受影響對象的Count deleteRequest.resultType = NSBatchDeleteResultTypeCount; // 使用NSBatchDeleteResult對象來接受返回結果,經過id類型的屬性result獲取結果 NSError *error = nil; NSBatchDeleteResult *result = [context executeRequest:deleteRequest error:&error]; NSLog(@"batch delete request result count is %ld", [result.result integerValue]); // 錯誤處理 if (error) { NSLog(@"batch delete request error : %@", error); } // 更新MOC中的託管對象,使MOC和本地持久化區數據同步 [context refreshAllObjects];
大多數狀況下,涉及到託管對象的操做,都須要將其加載到內存中完成。因此使用CoreData
時,須要注意內存的使用,不要在內存中存在過多的託管對象。在已經作系統兼容的狀況下,進行大量數據的操做時,應該儘可能使用批處理來完成操做。
須要注意的是,refreshAllObjects
是從iOS9
出來的,在iOS9
以前由於要作版本兼容,因此須要使用refreshObject: mergeChanges:
方法更新託管對象。
// 建立請求對象,並指明操做Employee表 NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Employee"]; // 建立異步請求對象,並經過一個block進行回調,返回結果是一個NSAsynchronousFetchResult類型參數 NSAsynchronousFetchRequest *asycFetchRequest = [[NSAsynchronousFetchRequest alloc] initWithFetchRequest:fetchRequest completionBlock:^(NSAsynchronousFetchResult * _Nonnull result) { [result.finalResult enumerateObjectsUsingBlock:^(Employee * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { NSLog(@"fetch request result Employee.count = %ld, Employee.name = %@", result.finalResult.count, obj.name); }]; }]; // 執行異步請求,和批量處理執行同一個請求方法 NSError *error = nil; [context executeRequest:asycFetchRequest error:&error]; // 錯誤處理 if (error) { NSLog(@"fetch request result error : %@", error); }
上面經過NSAsynchronousFetchRequest
對象建立了一個異步請求,並經過block
進行回調。若是有多個請求同時發起,不須要擔憂線程安全的問題,系統會將全部的異步請求添加到一個操做隊列中,在前一個任務訪問數據庫時,CoreData
會將數據庫加鎖,等前面的執行完成纔會繼續執行後面的操做。
NSAsynchronousFetchRequest
提供了cancel
方法,也就是能夠在請求過程當中,將這個請求取消。還能夠經過一個NSProgress
類型的屬性,獲取請求完成進度。NSAsynchronousFetchRequest
類從iOS8
開始可使用,因此低版本須要作版本兼容。
須要注意的是,執行請求時MOC
併發類型不能是NSConfinementConcurrencyType
,這個併發類型已經被拋棄,會致使崩潰。
好多同窗都問我有Demo
沒有,其實文章中貼出的代碼組合起來就是個Demo
。後來想了想,仍是給本系列文章配了一個簡單的Demo
,方便你們運行調試,後續會給全部博客的文章都加上Demo
。
Demo
只是來輔助讀者更好的理解文章中的內容,應該博客結合Demo
一塊兒學習,只看Demo
仍是不能理解更深層的原理。Demo
中幾乎每一行代碼都會有註釋,各位能夠打斷點跟着Demo
執行流程走一遍,看看各個階段變量的值。
Demo地址:劉小壯的Github
這兩天更新了一下文章,將CoreData
系列的六篇文章整合在一塊兒,作了一個PDF
版的《CoreData Book》,放在我Github上了。PDF
上有文章目錄,方便閱讀。
若是你以爲不錯,請把PDF幫忙轉到其餘羣裏,或者你的朋友,讓更多的人瞭解CoreData,衷心感謝!😁