您的位置:首页 > 移动开发 > Swift

iOS Swift教程 Core Data (四)Fetch进阶 上

2015-01-01 11:13 567 查看
前面三节中,我们使用了最简单的方法来保存或获取CoreData中的数据比如:获取所有的领结实例,但是有些时候,你可能想给予fetch更多的掌控,通过这节的学习,你将学会以下知识:

只获取你需要的数据;
使用predicates来优化fetch的结果;
在后台进程中进行fetch操作而不阻塞UI进程;
直接更新persistent store而减少不必要的fetch操作;

转载请注明出处:/article/9358011.html

源代码地址:https://github.com/dnawym/StudySwift/tree/master/CoreData/Bubble%20Tea%20Finder

NSFetchRequest:

前面章节中我们已经学到,NSFetchRequest实例用于从Core Data中获取数据,配置这个类的实例并将其交给NSManagedObjectContext,后后者会帮我们完成后续的工作。构造fetch request的方法有如下四种:

第一种:创建NSFetchRequest的实例和NSEntityDescription的实例,将entity赋值给fetch request

let fetchRequest1 = NSFetchRequest()
let entity = NSEntityDescription.entityForName("Person", inManagedObjectContext: managedObjectContext!)

fetchRequest1.entity = entity!
第二种:将entity的名称作为参数传递给fetch request,这样就不用显示的构造entity

let fetchRequest2 = NSFetchRequest(entityName: "Person")
第三种:使用NSManagedObjectModel来构造fetch request,这一节我们会使用这种方法

let fetchRequest3 = managedObjectModel.fetchRequestTemplateForName("peoperFR")
第四种:和第三种方法类似,但是会传递额外的参数给NSManagedObjectModel,这些额外的参数用于优化fetch结果

let fetchRequest4 = managedObjectModel.fetchRequestFromTemplateWithName("peopleFR", substitutionVariables: ["NAME" : "Ray"])
珍珠奶茶:

这款应用程序使用从Foursqure得到纽约市内30个珍珠奶茶点作为数据源(json),给据给定的过滤条件在table view中进行显示。

创建fetch request:

打开Model。xcdatamodeld左键按住Add Entity不放,选择弹出对话框中的第二个选项“Add Fetch Request”



这样就创建了我们需要的一个Fetch Request,我们暂时不需要为新创建的fetch request添加其它条件



接下来,我们在ViewController里面使用这个FetchRequest,添加变量的定义

import CoreData
var fetchRequest: NSFetchRequest!
var venues: [Venue]!


修改viewDidLoad,通过managed object model来获取我们创建的FetchRequest。fetchRequestTemplateForName使用字符串来查找我们使用model editor创建的FetchRequest

override func viewDidLoad() {
super.viewDidLoad()

fetchRequest = coreDataStack.model.fetchRequestTemplateForName("FetchRequest")

fetchAndReload()
}
fetchAndReload函数顾名思义,从core data获取数据并刷新table view

//MARK: - 辅助函数
func fetchAndReload() {
var error: NSError?
let results = coreDataStack.context.executeFetchRequest(fetchRequest, error: &error) as [Venue]?

if let fetchResults = results {
venues = fetchResults
} else {
println("Could not fetch \(error), \(error!.userInfo)")
}

tableView.reloadData()
}
修改tableView的2个函数numberOfRowsInSection和cellForRowAtIndexPath

func tableView(tableView: UITableView?, numberOfRowsInSection section: Int) -> Int {
return venues.count
}

func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {

var cell = tableView.dequeueReusableCellWithIdentifier("VenueCell") as UITableViewCell

let venue = venues[indexPath.row]

cell.textLabel!.text = venue.name
cell.detailTextLabel!.text = venue.priceInfo.priceCategory

return cell
}
程序执行效果如下:



获取不同类型的结果:

至此,你可能认为NSFetchRequest这个类是一个很简单的工具,设置一些属性,你就可以得到所需的数据。其实,NSFetchRequest像瑞士军刀一样,有这多种多样的功能。比如:获取一个单独的数据,计算统计结果(平均值、最小值和最大值等等)。

NSFetchRequest有一个名为resultType的属性,我们现在只是用了.Default值,除了这个值还有如下几种:
NSManagedObjectResultType:返回一个managed object(默认值)
NSCountResultType:返回满足fetch request的object数量
NSDictionaryResultType:
NSManagedObjectIDResultType:返回唯一的标示符而不是managed object

计算不同价位商家的数目:

修改FilterViewController.swift,添加最低价位的predicate函数:

// 这个NSPredicate用于计算属于最低价格区间的venues数量
lazy var cheapVenuePredicate: NSPredicate = {
var predicate = NSPredicate(format: "priceInfo.priceCategory == %@", "$")

return predicate!
}()


添加函数查询最低价位商家的数目并更新到Label上:

func populateCheapVenueCountLabel() {
let fetchRequest = NSFetchRequest(entityName: "Venue")
fetchRequest.resultType = .CountResultType
fetchRequest.predicate = cheapVenuePredicate

var error: NSError?
let result = coreDataStack.context.executeFetchRequest(fetchRequest, error: &error) as [NSNumber]?

if let countArray = result {
let count = countArray[0].integerValue

firstPriceCategoryLabel.text = "\(count) bubble tea pleases"
} else {
println("Could not fetch \(error), \(error!.userInfo)")
}
}
重载viewDidLoad函数

override func viewDidLoad() {
super.viewDidLoad()

populateCheapVenueCountLabel()
}
接下来添加中等价位和高价位商家的数量

lazy var moderateVenuePredicate: NSPredicate = {
var predicate = NSPredicate(format: "priceInfo.priceCategory == %@", "$$")

return predicate!
}()

func populateModerateVenueCountLabel() {
let fetchRequest = NSFetchRequest(entityName: "Venue")
fetchRequest.resultType = .CountResultType
fetchRequest.predicate = moderateVenuePredicate

var error: NSError?
let result = coreDataStack.context.executeFetchRequest(fetchRequest, error: &error) as [NSNumber]?

if let countArray = result {
let count = countArray[0].integerValue

secondPriceCategoryLabel.text = "\(count) bubble tea pleases"
} else {
println("Could not fetch \(error), \(error!.userInfo)")
}
}

lazy var expensiveVenuePredicate: NSPredicate = {
var predicate = NSPredicate(format: "priceInfo.priceCategory == %@", "$$$")

return predicate!
}()

func populateExpensiveVenueCountLabel() {
let fetchRequest = NSFetchRequest(entityName: "Venue")
fetchRequest.resultType = .CountResultType
fetchRequest.predicate = expensiveVenuePredicate

var error: NSError?
let result = coreDataStack.context.executeFetchRequest(fetchRequest, error: &error) as [NSNumber]?

if let countArray = result {
let count = countArray[0].integerValue

thirdPriceCategoryLabel.text = "\(count) bubble tea pleases"
} else {
println("Could not fetch \(error), \(error!.userInfo)")
}
}
更新viewDidLoad函数

override func viewDidLoad() {
super.viewDidLoad()

populateCheapVenueCountLabel()
populateModerateVenueCountLabel()
populateExpensiveVenueCountLabel()
}
程序filter页面效果如下,低价位商家27家,中等价位2家,高价位1家:



对fetch request进行计算:

首先计算deal的和,设置返回值类型是.DictionaryResultType

func populateDealsCountLabel() {
// 创建FetchRequest获取Venue对象并设置返回值类型为DictionaryResultType
let fetchRequest = NSFetchRequest(entityName: "Venue")
fetchRequest.resultType = .DictionaryResultType

// 创建NSExpressionDescription来请求进行总和计算,取名为sumDeals,一会儿就可以通过这个名字
// 从fetch请求返回的字典中找到总和
let sumExpressionDesc = NSExpressionDescription()
sumExpressionDesc.name = "sumDeals"

// 指定要进行总和计算的字段名-specialCount并设置返回值类型
sumExpressionDesc.expression = NSExpression(forFunction: "sum:", arguments:[NSExpression(forKeyPath: "specialCount")])
sumExpressionDesc.expressionResultType = .Integer32AttributeType

// 设置fetchRequest的propertiesToFetch属性为我们构造的sumExpressionDesc告诉fetchRequest
// 我们需要对数据进行求和
fetchRequest.propertiesToFetch = [sumExpressionDesc]

// 执行fetch request,将返回结果转换为optional字典,使用设置的字段名-sumDeals来索引求和的结果
var error: NSError?
let result = coreDataStack.context.executeFetchRequest(fetchRequest, error: &error) as [NSDictionary]?

if let resultArray = result {
let resultDict = resultArray[0]
let numDeals: AnyObject? = resultDict["sumDeals"]
numDealsLabel.text = "\(numDeals!) total deals"
} else {
println("Could not fetch \(error), \(error!.userInfo)")
}
}



最后一种返回值类型是.ManagedObjectIDResultType,这种类型的fetch结果返回值是NSManagedObjectID的数组,类似于数据库的主键。
由于每一次fetch,可能返回大量的数据,这样每一个fetch都会消耗大量的内存,接下来我们使用Predicate来限制返回的数据的数量。
为FilterViewController类添加protocol,这样实现了这个protocol的类在用户修改了排序或过滤条件后就会被通知到。

protocol FilterViewControllerDelegate: class {
func filterViewController(filter: FilterViewController,
didSelectPredicate predicate: NSPredicate?,
sortDescriptor: NSSortDescriptor?)
}
添加delegate,选定的NSSortDescriptor和NSPredicate的定义

weak var delegate: FilterViewControllerDelegate?
var selectedSortDescriptor: NSSortDescriptor?
var selectedPredicate: NSPredicate?
修改saveButtonTapped函数和didSelectRowAtIndexPath。当用户点击价格分类cell时,更新selectedPredicate。当用户点击search按钮时,调用代理的filterViewController的函数。
//MARK - UITableViewDelegate methods

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {

let cell = tableView.cellForRowAtIndexPath(indexPath)!

switch cell {
case cheapVenueCell:
selectedPredicate = cheapVenuePredicate
case moderateVenueCell:
selectedPredicate = moderateVenuePredicate
case expensiveVenueCell:
selectedPredicate = expensiveVenuePredicate
default:
println("default case")
}

cell.accessoryType = .Checkmark
}

// MARK - UIButton target action

@IBAction func saveButtonTapped(sender: UIBarButtonItem) {

delegate!.filterViewController(self, didSelectPredicate: selectedPredicate, sortDescriptor: selectedSortDescriptor)

dismissViewControllerAnimated(true, completion:nil)
}
回到ViewController,实现代理函数

//MARK: - FilterViewControllerDelegate methods
func filterViewController(filter: FilterViewController, didSelectPredicate predicate: NSPredicate?, sortDescriptor: NSSortDescriptor?) {
fetchRequest.predicate = nil
fetchRequest.sortDescriptors = nil

if let fetchPredicate = predicate {
fetchRequest.predicate = fetchPredicate
}

if let sr = sortDescriptor {
fetchRequest.sortDescriptors = [sr]
}

fetchAndReload()
}
程序执行效果如下,在过滤页面选择只显示中等价位的商家:



类似的方法,实现其它三种predicate

lazy var offeringDealPredicate: NSPredicate = {
var pr = NSPredicate(format: "specialCount > 0")
return pr!
}()

lazy var walkingDistancePredicate: NSPredicate = {
var pr = NSPredicate(format: "location.distance < 500")
return pr!
}()

lazy var hasUserTipsPredicate: NSPredicate = {
var pr = NSPredicate(format: "stats.tipCount > 0")
return pr!
}()
更新didSelectRowAtIndexPath函数

//MARK - UITableViewDelegate methods

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {

let cell = tableView.cellForRowAtIndexPath(indexPath)!

switch cell {
case cheapVenueCell:
selectedPredicate = cheapVenuePredicate
case moderateVenueCell:
selectedPredicate = moderateVenuePredicate
case expensiveVenueCell:
selectedPredicate = expensiveVenuePredicate
case offeringDealCell:
selectedPredicate = offeringDealPredicate
case walkingDistanceCell:
selectedPredicate = walkingDistancePredicate
case userTipsCell:
selectedPredicate = hasUserTipsPredicate
default:
println("default case")
}

cell.accessoryType = .Checkmark
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: