We have already come to the end of the core data series tutorial, where we discuss how to use Nsfetchedresultscontroller to optimize our applications, improve the speed of applications, and reduce their memory footprint.
Have you forgotten what you said before? Let's review it in the first tutorial: iOS Tutorial: Core Data Persistence Storage Basics Tutorial We talked about how to create a data model and testing method for an iOS program, and we also connected the data model to a table view as a data source.
In the second Tutorial: iOS Tutorial: How to preload and ingest data using core data–, we talked about how to parse data files in different formats into a single core data-universal SQLite database, and how to port this database to our iOS project. So that our app has some initial data.
You can download the second part of the source code from here.
Why use Nsfetchedresultscontroller?
So far, we're like using SQLite3, because core data is essentially manipulating the SQLite database, but we're writing less code than directly using SQLite, and it's easier to use a variety of database functions.
However, we also have a very good core data feature is not used, this feature can greatly improve the performance of our program, he is: Nsfetchedresultscontroller.
Now in our example program, we're all going to load all the data into the view, which may be acceptable for our application, but if an application has a large amount of data, the loading speed will become slow and will have an impact on the user experience.
Ideally, we only load the part of the data that the user is viewing, and fortunately Apple has already provided a way to do so, that is, Nsfetchedresultscontroller.
So, let's first open the FBCDMasterViewController.h, put the previous Failedbankinfos, this nsarray array of lightning, add a nsfetchedresultscontroller instead of it:
@interface Fbcdmasterviewcontroller:uitableviewcontroller@property (Nonatomic,strong) nsmanagedobjectcontext* Managedobjectcontext, @property (nonatomic, retain) Nsfetchedresultscontroller *fetchedresultscontroller; @end |
In the synthesize section of FBCDMASTERVIEWCONTROLLER.M, delete the previous Failedbankinfos synthesize statement and join:
@synthesize Fetchedresultscontroller = _fetchedresultscontroller; |
Another cool feature of Nsfetchedresultscontroller is that you can re-declare it as nil in viewdidunload. This means that this method has an automatic memory management mechanism, which means that when the content is not in the screen, its memory is automatically emptied. To do all this, all you need to do is declare it empty in Viewdidunload.
-(void) Viewdidunload { self.fetchedresultscontroller = nil;} |
Well, now to the interesting part, we start to create the controller that gets the data. First, we declare a property, so that it can be detected as the program runs, and if it does not exist, it is created.
Add the following code to the head of the file:
-(Nsfetchedresultscontroller *) Fetchedresultscontroller {if (_fetchedresultscontroller! = nil) {return _fetch Edresultscontroller; } nsfetchrequest *fetchrequest = [[Nsfetchrequest alloc] init]; Nsentitydescription *entity = [nsentitydescription entityforname:@ "Failedbankinfo" Inmanagedobjectcontext:managedob Jectcontext]; [Fetchrequest setentity:entity]; Nssortdescriptor *sort = [[Nssortdescriptor alloc] initwithkey:@ "Details.closedate" ascending:no]; [Fetchrequest Setsortdescriptors:[nsarray Arraywithobject:sort]; [Fetchrequest setfetchbatchsize:20]; Nsfetchedresultscontroller *thefetchedresultscontroller = [[Nsfetchedresultscontroller alloc] InitWithFetchRequest: Fetchrequest managedobjectcontext:managedobjectcontext sectionnamekeypath:nil cacheName:@ "Root"]; Self.fetchedresultscontroller = Thefetchedresultscontroller; _fetchedresultscontroller.delegate = self; return _fetchedresultscontroller;} |
This code is very similar to what we used in the Viewdidload method, creating a fetch request, failedbankinfo the object, and so on, but there are still some new things we want to discuss.
First, when we use Nsfetchedresultscontroller, we have to set up a data classifier to assign a fetch request, and the data classifier is the way we tell core data about the bedding and storage we want to introduce.
The need for this data classifier is that it not only can orchestrate all the returned properties and data, but it can also orchestrate all the properties and data associated with it, as if a genius were doing it. If we want to orchestrate the data based on the close date attribute in Failedbankdtails, but want to receive all the data in the Failedbankinfo, Core data can do this with this feature.
The next statement is very important, is to set the maximum value of the buffer value of the obtained data, which is why we want to use this feature in this scenario, so that the fetched method will automatically get the value of the data item set, and then when we look down, the program will automatically get a variety of data.
When we set the fetch buffer value, we finished creating the Nsfetchedrequestcontroller and passed it to the fetch request, but this method actually has the following parameters:
- For managed object content, we value passing content.
- Section name Key path allows us to group the contents of the data by the Magic attribute.
- The cached name of the file name should be used to handle any repetitive tasks, such as setting up grouping or arranging data.
Now that we've completely created a way to get some of the data, let's change the way we used to add data to the data we've been using.
-(void) viewdidload { [super viewdidload]; Nserror *error;if (![ [Self Fetchedresultscontroller] performfetch:&error]} {//Update to handle the error appropriately. NSLog (@ "unresolved error%@,%@", error, [error userInfo]); exit ( -1); Fail} self.title = @ "Failed Banks";} |
We are here to operate our Fetchedresultscontroller and execute the Performfetch method to get the first batch of data buffered.
After that, update the Numberofrowsinsection method
-(Nsinteger) TableView: (UITableView *) TableView numberofrowsinsection: (nsinteger) section { ID Sectioninfo = [[_fetchedresultscontroller sections] objectatindex:section]; return [Sectioninfo numberofobjects];} |
To update the Cellforrowatindexpath method:
-(void) Configurecell: (UITableViewCell *) cell Atindexpath: (Nsindexpath *) Indexpath { Failedbankinfo *info = [_ Fetchedresultscontroller Objectatindexpath:indexpath]; Cell.textLabel.text = Info.name; Cell.detailTextLabel.text = [NSString stringwithformat:@ "%@,%@", info.city, info.state];} -(UITableViewCell *) TableView: (UITableView *) TableView cellforrowatindexpath: (Nsindexpath *) Indexpath { static NSString *cellidentifier = @ "Cell"; UITableViewCell *cell = [TableView dequeuereusablecellwithidentifier:cellidentifier]; Set up the cell ... [Self Configurecell:cell atindexpath:indexpath]; return cell;} |
Now we'll divide the previous logic into separate Configurecell methods, which we'll use later.
And one last thing, we need to set a proxy method for Nsfetchedresultscontroller, the good news is that there are templates, in fact, I copied from an Apple official example program, the following methods are added to the bottom of the file:
-(void) Controllerwillchangecontent: (Nsfetchedresultscontroller *) Controller {//The Fetch controller is about to star T sending change notifications, so prepare the table view for updates. [Self.tableview beginupdates];} -(void) Controller: (Nsfetchedresultscontroller *) controller didchangeobject: (ID) anobject Atindexpath: (Nsindexpath * ) Indexpath Forchangetype: (nsfetchedresultschangetype) type Newindexpath: (Nsindexpath *) Newindexpath {UITableView * TableView = Self.tableview; Switch (type) {case Nsfetchedresultschangeinsert: [TableView insertrowsatindexpaths:[nsarray Arraywithob Ject:newindexpath] Withrowanimation:uitableviewrowanimationfade]; Break Case Nsfetchedresultschangedelete: [TableView deleterowsatindexpaths:[nsarray Arraywithobject:indexpath] WithRo Wanimation:uitableviewrowanimationfade]; Break Case nsfetchedresultschangeupdate: [Self configurecell:[tableview Cellforrowatindexpath:indexpath] Atindexpath:indexpath]; Break Case Nsfetchedresultschangemove: [TableView Deleterowsatindexpaths:[nsarrayarraywithobject:indexpath] WithRowAni Mation:uitableviewrowanimationfade]; [TableView Insertrowsatindexpaths:[nsarrayarraywithobject:newindexpath] Withrowanimation: Uitableviewrowanimationfade]; Break }}-(void) Controller: (Nsfetchedresultscontroller *) controller didchangesection: (ID) sectioninfo Atindex: (Nsuinteger ) Sectionindex Forchangetype: (nsfetchedresultschangetype) type {switch (type) {case Nsfetchedresultschangeinsert: [Self.tableview insertsections:[nsindexset Indexsetwithindex:sectionindex] Withrowanimation:uitableviewrowanim Ationfade]; Break Case Nsfetchedresultschangedelete: [Self.tableview deletesections:[nsindexset Indexsetwithindex:sectionindex] W Ithrowanimation:uitableviewrowanimationfade]; Break }}-(void) Controllerdidchangecontent: (NsfetchedresultsconTroller *) Controller {//The FETCH controller has sent all current change notifications, so tell the table view to Pro Cess all updates. [Self.tableview endupdates];} |
Now that the compiler runs your app, it should look the same on the surface, but if you look at the console, something amazing is happening:
SELECT 0, T0. Z_PK from Zfailedbankinfo t0 left OUTER joins zfailedbankdetails T1 on t0. Zdetails = t1. Z_PK ORDER by T1. Zclosedate desctotal Fetch execution time:0.0033s for 234 rows. SELECT 0, T0. Z_PK, t0. Z_opt, t0. Zname, t0. Zstate, t0. Zcity, t0. Zdetails from Zfailedbankinfo t0 left OUTER joins zfailedbankdetails T1 on t0. Zdetails = t1. Z_PK WHERE t0. Z_PK in (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) ORDER by T1. Zclosedate DESC LIMIT 20total fetch execution time:0.0022s for rows. SELECT 0, T0. Z_PK, t0. Z_opt, t0. Zname, t0. Zstate, t0. Zcity, t0. Zdetails from Zfailedbankinfo t0 left OUTER joins zfailedbankdetails T1 on t0. Zdetails = t1. Z_PK WHERE t0. Z_PK in (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) ORDER by T1. Zclosedate DESC LIMIT 20total fetch execution time:0.0017s for rows.
As you can see, behind the scenes, Nsfetchedresultscontroller is getting a large number of IDs from the order they were set up before Failedbankinfo, buffering only a certain number of items at a time, just as we expected.
If you use the SQLite database directly, there will be a lot of work to do, so why not use core data to save time?
What do you see later?
This is the example of my production process source code, welcome to download.
This is the original author's sample program: Project here (direct download).
Please pay attention to my scarf: @Oratis
On the Oratis and the watercress, my name is the same.
I'll share the tutorials I've published later in these social networks.
If you have any questions, welcome to the bottom of the message, also welcome to write to me, my e-mail address is: [email protected]
iOS Tutorial: How to use Nsfetchedresultscontroller