Subindex build by Linnet http://linnet.126.com http://blog.csdn.net/redbirdli 火鳥字幕合併器SubindexV0.5 Created time 14.11.15 16:44:03
斯坦福大学 > Stanford University
欢迎来到CS193P课程 2013-14秋季第13讲 > Welcome to number 13 of Stanford CS193p, the fall of 2013-14,
今天 我打算讲下期末项目的要求 and today, I'm going to go over the requirements for your final project,
然后我将再用几张幻灯片 and then I'm going to talk a little more, a few more slides
讲解Core Data about Core Data,
以及它如何同UITableView关联在一起 and how it hooks up with UI TableView,
这两者可以说是天生一对 because those two are a match made in heaven,
然后将是巨大的Core Data和TableView的演示 and then I'm going to do this gigantic Core Data with TableViews demo.
期末项目 Alright, so you're final projects.
期末项目分两阶段 第一阶段是提交提案 There's kind of two phases to your final project; one is you have to submit a proposal
我们会审核 for us, which we'll review.
我们审核的主要是范围 We're mostly just reviewing it for scope,
确保你选的项目没有太大或是太小 to make sure you haven't picked too big a project or too small a project.
我们不会去查看你提案中的每一个细节 Okay, we're not, you know, looking in detail of every single thing you're going to do.
我们只打算检查你的大方向对不对 We're just trying to make sure you're headed in the right direction.
在期末项目的过程中 Because over the course of the final project,
你们将和我们进行交流 向我们提问题 you're going to of course talk to us, and ask us questions and interact
过程中获得一些点子 最终很好地达成目的 with us to kind of get an idea of getting, you know, hitting a good target at the end.
请快速完成这一阶段 So, that is really, do immediate.
想好要做什么之后 We'd like you to get us your proposal. If you know what you're going to do,
请尽快把提案交给我们 please submit a proposal as soon as possible.
还没想好的人要快想 并尽快提交 If you don't get on it right away, and submit it as soon as possible, really though
下周三将是提交的截止日期 by next Wednesday would be the latest we'd want to hear from you.
我很不想这样 不过你们很多人 And, I hate to say that, because I know a lot of you wait
都喜欢把事拖到最后一天再定 我很理解 until the day before deadline to do anything, and I understand
但是这个提案需要越早越好 that impulse, but this is something where you want to get
这样就算出现问题 我们也有时间找你 that proposal in sooner, in case we have issues, and we can get back to you,
如果这个方案不行 你可以写一个新的提案 and you either write, maybe come up with a whole new proposal if what you're thinking
如果这个方案不行 你可以写一个新的提案 about is just not going to work.
然后第二阶段是期末项目本身 Then the second phase is the final project itself,
这个项目的代码 and that project, the code for the project,
还有关于项目的两分钟主题演讲幻灯片 along with the keynote, set of keynote slides for a two minute presentation about your
定在12月6日周五交 project are due on Friday, December 6.
看起来挺远的 有一个月时间 So, that seems like that's far away, I know, that's over a month away, right.
现在是11月6日 但这中间有感恩节 It's November 6 right now, but you got Thanksgiving in there,
其实并没有多长时间 这个期末项目 so it's really not that far away, and so your final project,
同其它课中的一样 like a final project in any class,
你有很多周时间来做 where you get multiple weeks to do it, you do not want to wait
千万别等到最后一周才开始做 to the last week to get started.
请尽快开始 So, get started immediately.
到现在 创建一个应用程序所需的知识你们都已经学了 You have everything you need now to start building an application.
对 期末项目不允许迟交 Yeah, no late days on the final project at all. Simply will not accept it late.
12月6日午夜 做成怎样都得上交 Whatever you have on December 6 at midnight, submit it.
因为我只接受这之前上交的东西 I don't care what it is; submit it, because that's all. I'm not going to accept anything.
因为学生太多了 I just can't, because I've got a lot of students
我要给所有人评分 who are taking this for a grade especially, and that's a lot
要保证公平 我需要很长时间 of time I have to be fair to those people, and also,
因此 我需要尽快开始评分 I have to get started grading immediately.
你们的陈述将是两分钟 The presentation that you're going to do, two minutes,
你需要给我们和同学们讲解项目的要点 it's really just going to be pitching your project to us, to your fellow classmates.
你要把这想成争取风投资金 If you can imagine, it's like you're trying to pitch a VC
让风投投资到你的公司或你的app to invest in your company or to do the app,
或者 你要说服别人买你的东西 or you're just trying to get people to buy it, or whatever.
陈述要达到这个效果 具体的你们自己看着办 It's that kind of presentation. I'll leave it up to you.
如果你想 你也可以实况演示程序 You can certainly do a live demo if you want.
你需要iPhone 4S及以上或iPad 2及以上的设备 You'll need iPhone 4S or later, or you'll need an iPad 2 or later, in order to hook
来使用这个奇妙的投影系统 使用Apple TV Mirroring up to this wonder projection system we have using Apple TV mirroring.
不过陈述并不一定要是实况演示 But, you don't have to do a live demo.
实况演示很危险 我在课上就经常出错 Live demos are perilous, as you can see, what I do each lecture,
不过实况演示很高效 but they also kind of can be effective,
能够很好地展示你的app 你们自己决定 and a really good way to show off your app, so it's kind of up to you.
项目陈述是必须的 And, that presentation is required.
我还有一个备选陈述时间 I will have an alternate presentation time,
也就是季度的最后一堂课 which is the last lecture of the quarter,
也就是期末考时段前的那一周 which is the week before the final exam period.
这是这个 So, that's that.
程序大小是三周作业那么大的工作量 Yeah, the scope is basically three weeks of homework worth of work.
你们现在应该知道这大概是多少 You should know by now approximately what that is.
记得原来 学生需要同时通过作业 Remember in the past when the people have to pass both the homework section
和期末项目 and the final project separately.
你只要一个不合格 就算另一个得A 你也过不了这门课 You can't bomb one, and get an A on the other, and pass this class.
期末项目需要能在硬件上奏效 Your final project has to work on hardware.
你需要证明 可以是在2分钟的demo演示中 You have to show it working, either in a demo in your two minutes,
如果没有2分钟demo演示 你就需要展示给助教 or if you don't show a live demo in your two minutes, you have to show your TA.
只算iOS-SDK代码 如果你的app有后台 Only iOS-SD code counts, so if your app has a backend,
某处有服务器 这方面你不会额外得分 some server somewhere, you get no credit for any of that really.
你只能从iOS代码得分 You're only getting credit for iOS code, okay.
有后台你可以模拟 So, simulate your backend if you have to,
当然 其它某处有后台 那也没问题 or if you have the backend from somewhere else, that's fine,
不过 不要浪费时间写非iOS代码 but don't waste time doing non-iOS code
毕竟只有三周时间 非iOS代码没有任何额外加分 in this three weeks, because you're not going to get any credit for that work.
评分将基于你对SDK的适当使用 And, you'll be graded on your proper use of the SDK,
还有合适的面向对象编程以及代码优雅性 and also proper object-oriented programming, and your aesthetics will matter.
不要写一大堆垃圾UI 这会让程序看起来很糟糕 So, don't put a big junky-looking UI that just like, really looks terrible, and yeah,
别分心去写非iOS代码 don't get sidetracked on non-iOS code.
这同之前讲的重复了 That's kind of a repeat of all that slide there, so.
项目陈述质量也是评价范围 不过占比很小 The presentation quality matters a tiny bit. It does matter.
有效陈述是一个很重要的技能 Giving your presentation, effective presentation is a very important skill to have.
斯坦福学生毕业的时候都应该擅于演讲陈述 All of you at Stanford should be good at that by the time you get
这里我又提供了一个机会 out of here, so here's just another chance
让你练好的演讲陈述 to really practice doing a good presentation.
计下时 看你花了多久 Time it; see how long it's going to take.
演讲之前务必演练几次 You know, run through it a couple times practicing.
提案中需要有两部分 You proposal needs to have 2 sections in it.
第一部分是关于你做了什么的总体描述 Okay, the first section is kind of an overall description. What am I doing?
这个用例子来讲最好 这里就是一个例子 This is best shown by example, so I have this example here,
这是一个莎士比亚导演程序 which is a Shakespeare Director App,
假设你是一家戏剧公司的导演 so let's say you're a director at a theater company,
你要导演莎士比亚的戏剧 and you're going to be direction some Shakespeare plays,
这个程序能够帮你 and this is an app to help you do that.
它能设法从Folio数据库中找出一段莎士比亚戏剧 And so, it has a way to bring up a Shakespeare play from the folio database,
然后进行布局 and then you can lay out the blocking,
像场景设在哪里 which is like where the scene is set up,
人站在哪 所有这些东西 where the people are standing and all that stuff of each scene,
同对话保持一致 lined up with the dialogue.
然后 它还有一个对话学习模式 And then, it's also got a dialogue learning mode
给要学习对话的人 学习他们的对白 for people who want to learn the dialogue, learn their lines.
它可以说出其它部分 你则需要说出你自己的部分 It will say the other parts, and then you can say your own part.
这里描述的是程序能做什么 So, you can see that this is kind of a description of what it does,
而不是你怎么做 as opposed to how you're going to do it.
第二部分是 你要用哪些iOS部分来实现它 Section 2 is what parts of iOS are you going to use to implement it.
它有TableView 使用自定义单元格 Okay, it's going to have TableView with custom cells.
会用到摄像头 It's going to use the camera.
有文本字段在弹窗里面 It's going; you know text fields with in popovers.
用AVFoundation It's going to use AV foundation.
有NSTimer It's going to have NS Timer.
有Core Data It's going to have Core Data,
我的实体会在这里 and where there are going to be my entities.
将对话这些东西打印到打印机 It's going to print out the blocking things to printer,
这是我们课上没讲的东西 which is something we don't cover in class.
我们要求你使用至少一种课上没讲过的特性 And, you're required to have one feature, at least, that it was not covered in lecture.
这是给你们的一个任务 Okay, and so that task is on there;
让你必须自己从说明文档中去学东西 not only so you have to learn something from a documentation,
而不是都由我来给你们讲 instead of my kind of getting you started with it,
这还能让你仔细去看iOS里面还有些什么 but also so that you peruse all of iOS and find out what's in there.
不要找我问 你能否告诉我一个 So, don't come to me and say, oh, can you tell me a feature
课堂上没讲的特性用于项目 to do for non-iOS not covered in lecture?
要知道 这就是你任务的一部分 It's like, that's part of the task for you to go figure
去找找并选出某些特性 out what's out there and pick something.
这就是提案中需要包括的两大部分 So, that's what your proposal needs to have, these two sections.
你要做什么 What it is you're opting to do,
你要用到哪些已经学过的iOS API and then what iOS APIs you're going to use that you know of right now to implement.
这里有一些注释 And, here's some notes,
这些幻灯片 你们都可以在线下重新看 again, you're going to review these slides offline to feel
看怎么写一份好的提案 really get a good for what makes a good proposal,
你还可以到Piazza上要求我们进一步阐明 and you can ask on Piazza for clarifications, etc.
下面我要讲的还是Core Data Okay, so, the next topic I want to talk about is Core Data still,
关于Core Data与TableView如何共同协作 but its Core Data and TableView. How Core Data and TableView go together.
Core Data是一系列对象 Because Core Data is a bunch of, you know, objects,
一个大的对象图 a big object graph,
而TableView在遍历对象图方面很擅长 and TableViews are really good for traversing through object graphs.
怎么做呢 So, how do we do this?
iOS中有一个很好的类 There's a great class in iOS
叫NSFetchedResultsController(取回结果控制器) called NSFetched Results Controller.
这个类的唯一作用是将一个NSFetchRequest This class' only purpose in life is to link an NSFetchRequest,
这是两天前的内容 但愿你们记得 which hopefully you'll remember from two days' ago lecture
和一个UITableView关联起来 with a UI TableView. That's what it does,
它的作用是将这两者关联起来 这样一来 it basically bonds those two things together, so that
Fetch取回的任何东西都总会显示在TableView中 the Fetch; anything that Fetch would be fetching is always showing in the TableView,
哪怕数据库在FetchRequest之下发生改变 even if the database is changing underneath that FetchRequest.
它仍然会更新TableView Okay, it's still going to be updating the TableView.
工作方式方面 FetchedResultsController有两部分 So, the way this works, it's really two parts for the Fetched Results Controller.
一是 它回答了 One is it answers
UITableViewDataSource协议中的所有问题 all the questions in the UI-TableView data source protocol,
例如有多少行 多少section like how many sections, how many rows and sections,
甚至一些你们不知道的进阶问题 and even some ones that you don't know about, some more advanced ones.
这些它都回答了 It answers those questions as well.
它可以使用这样的代码回答所有这些问题 So, it's able to answer all those questions, using code kind of like that.
此外 它还能在任何给定时刻告诉你 Also, it can tell you at any given time,
数据库中的什么实体显示在给定行中 what thing in your database, what entity in your database is being shown in a given row.
表格中的行同数据库中的对象之间有一一映射关系 There's a one to one mapping between a row in the table and some object in the database.
我们知道进行FetchRequest时 只会返回特定类型的对象 Alright, of course we know when you do a FetchRequest, it can only return objects
一个对象的数组 of a certain kind; an array of objects.
这会让你得到它 And so, this will let you get it.
理解这个方法很重要 也就是objectAtIndexPath And, it's a very important method to understand, which is objectAtIndexPath.
你将这发给FetchedResultsController Okay, you send that to the Fetched Results Controller,
它会返回一个Photo *给你 或Photographer and it will return you a photo star, or a photograph photographer star
或某个NSManagedObject * 这是那一行处的对象 or some NS Managed ObjectStar, which is the object that's at that row,
然后你可以抽出属性 and then you can pull out the attributes,
将它们放到UITableViewCell and put them in to your UI-TableView cell
使用cellForRowAtIndexPath in cellForRowAtIndexPath.
理解这个方法很重要 It's really important to understand that method.
如何创建这种NSFetchedResultsController呢 How to you create one of these NS Fetched Results Controller.
它的alloc init是这样的 Okay, its Allocinit looks like this.
它取一个FetchRequest 一个context It takes a FetchRequest, a context.
这显然是我们要进行取回的位置 That's obvious where we're going to be doing the fetching.
它还可以处理section header It will even do section headers,
你指定你要取回的对象中 so you specify which attribute in the objects
哪个属性是section的名称 that you're fetching is the section, the name of the section,
然后它会将表格分成section and then it will divide the table into sections.
它还可以进行缓存 And, it can also do caching, okay, so we'll talk
等下我会讲到后面那两点 about those last two things in a minute here.
先看我们要创建哪种FetchRequest But, let's take a look at what kind of FetchRequest we might create
并放入到FetchedResultsController to put into a Fetched Requests Controller,
这里我创建了一个照片请求 我要取回照片 so here I'm creating a photo request, so I'm going to be fetching photos.
我需要一个排序描述器 I need a sort descriptor
说明这些照片在TableView是什么顺序 that says what order these photos are going to be in in the TableView,
假设我要按标题排序 so I'll sort them by their title, let's say.
然后是谓词 And, then a predicate.
我想取回的所有照片满足 I'm going to get all the photos
具有给定名字为photogName的拍照者 that were taken by a photographer with a given name, photogName.
这就指明了谓词 Okay, so I just specify the predicate.
我这里只是创建了一个普通的NSFetchRequest So, you I just basically create a normal NS FetchRequest,
然后我使用这个FetchedResultsController alloc and then I just do this FetchedResultsController alloc,
FetchRequest context FetchRequest context,
sectionNameKeyPath和cache section key name and cache.
就这么简单 It's as simple as that.
然后讲下最后两项 Now, oh, yeah, so, those last two items.
cache 关于缓存的有一点 如果你指定nil 它不会缓存 The cache by way, one thing about the cache, if you specify nil, it won't cache.
缓存是说 它会缓存取回的结果 Caching means that it will cache the results of that fetch
在你app启动之间 between launching of your app.
换言之 它会永久性地在磁盘上进行缓存 In other words, it will permanently, you know, on disc, cache it.
而不是在内存中缓存 This is not caching it in memory.
这里总是内存内的缓存 You know, it's always going to do it in memory caching.
Core Data会这样 Core Data does that.
这是在启动之间 So, this will make so between launches,
它会有结果缓存 但如果你设置它为非nil it has this result cached, but if you set that to non-Nil,
你最好让FetchRequest保持完全一样 okay, you better keep your FetchRequest exactly the same.
如果FetchRequest中改了任何东西 If you change anything about your FetchRequest, and come back
然后尝试使用缓存 它就会失效 and try to use your cache, it's going to fail.
这只针对总有相同FetchRequest的TableView So, this is only really for TableViews that have the same Fetch Request all the time,
完全相同的谓词和描述器 exact same predicates or descriptors.
一切都相同 Everything's the same.
如果你要使用sectionNameKeyPath If you're going to use that section key thing,
尝试把section放到TableView中 try and put sections in your table view,
排序描述器就需要匹配section键 the sort descriptors have to match up with the section keys.
换言之 表中的行 In other words, the rows in the table,
你取回的照片 the photos that you fetch,
必须和section header具有相同顺序 have to be in the exact same order that section headers would be,
这是一个常规的TableView东西 and this is a normal TableView thing, right?
section header总要同行使用相同顺序 The section headers always have to be in the same order as the rows.
这里也是一样 Well, that's true here too.
几乎总有 你的第一个排序描述器 So, almost always, your first sort descriptor will be the
是section键header section key header,
确保一切都是按照相同顺序排序 to make sure everything is sorting in the same order.
FetchedResultsController还有一个委托 The Fetched Results Controller also has a delegate,
使用这个委托 它能监视Core Data中发生的事情 and using that delegate, it can watch what's happening in Core Data,
当有东西变化时 FetchRequest会受影响 and when something changes that would affect your FetchRequest,
你的表格会变化 这是很了不起 it will change your table, which is incredibly cool.
也就是说 如果你添加一张照片 Okay, that means if you added a photo,
它匹配你的FetchRequest and if it would have matched your FetchRequest,
表格中也会随之增加一行 it will add a row to the table,
你什么都不用做 因为它在监视 and you don't have to do anything, because it's watching.
它在使用NSObjectManagedContext It's using that NS Object Managed Context,
didChangeObject广播站 它在监听这个 objects did change radio station, it's listening to that,
它会用这样的方法相应做出变化 and it's changing it with methods like this.
这里有两点 So, there's really two things
来让FetchedResultsController同表格关联起来 to get a Fetched Request Controller hooked up to your table.
一是使用它 One is, you've got to use it
实现所有UITableViewDataSource的东西 to implement all those UI-TableView data source things,
二是设置它的委托 and two, is you've got to set its delegate,
然后使用所有这些方法来监视 and then use all these methods to have it watch,
不过我帮了你们一把 but we've made that easy for you.
我创建了一个类叫CoreDataTableViewController I've created a class called Core Data TableView Controller,
我会把它发给你们 and I'm going to make it available to you.
它会做到上述两件事 欢迎你们看看它的实现 All it does it those two things, and you're welcome to look at the implementation of it.
几乎都是一行代码 It's all pretty much just one liners
使用FetchedResultsController来实现数据源 that are just using the Fetched Results Controller to implement the data source,
然后做这些委托的事情 and to do the delegate business.
使用FetchedResultsController时你只需要知道 And, the only thing you need to know to use Core Data TableView Controller,
它有一个属性叫fetchResultsController it has a property called Fetch Results Controller
你把这个设为一个FetchedResultsController就行了 and you just set that to a Fetch Results Controller, and it will just work.
不需要做别的事了 Nothing else required to do.
设定这个后 CoreDataTableViewController就会 You just set that, and Core Data TableView Controlled will then
使用它来回答所有的数据源问题 use it to answer all those data source questions,
并设置FetchedResultsController的委托 and also it will set the delegates of the Fetched Results Controller,
让它合适地监视数据库 and make it so it watches the database properly.
这会在你们的作业中出现 So, that will be included in your homework.
作业中要用到 今天的演示或许也会用到 You will definitely want that for your homework. I will be using that in the demo today.
好 讲到演示 Alright, so speaking of the demo.
这是一个很大的演示 This is a huge demo.
或许我都做不完 I may not get all the way through it.
其中包含了一些课上还没讲到的东西 It covers some things that I haven't covered in lecture yet,
这是故意的 and that's kind of intentionally.
只有这一讲中我这样做了 This is the only lecture I really do that,
我只是想给你们展示一些 but I just wanted to kind of show you some things
我不想专门花课上时间讲解的东西 that I didn't really want to spend lecture time on.
以后的课上我可能会回顾这些 无论如何 I might come back to these in future lecture, but anyway,
这里有很多东西要讲 there's a lot, lot there to cover,
而你们有这些幻灯片可以用 and you have these slides in front of you.
但愿你们能够看清这个列表 Hopefully you can see what the list there is.
等下我就不开幻灯片了 先看以后的展望 I'm not going to back to the slide, so coming up;
今天是最后一次作业 today's your last homework.
我们有Instruments要讲 它是Xcode的性能监控助手 We have an instrument, which is performance monitoring adjunct
我们会在周五课外辅导讲到 然后下周 to X code on Friday's section, and then next week,
我们会简单谈到多任务处理 we're going to talk a little bit about multitasking.
我会更多讲到 因为今天演示中我会谈到一些 I say more multitasking, because I'm going to do some of that in the demo today,
然后我们会讲一些更进阶的segue and then we're going to do some more advancing segue.
我们之前所讲的segue只是 So far the only segueing we've done is,
UINavigationController push segue UI-Navigation Controller pushed segues basically,
之后我还讲了一点iPad replace segue and then also, we talked a little bit about iPad replace segues,
替换掉整个detail视图 这个segue比较奇特 where you replace the entire detail view, which is kind of a weird segue.
以后我们还会谈到更多的segue We're going to talk about some more kinds of segues that we can do as well.
或许下周 或许下下周 我们还会讲到MapKit And, maybe we'll get to Mapkit next week or maybe the week after.
在进入这个巨型demo之前 有任何问题吗 Alright, any questions before I dive into this monster demo?
请随时打断我 Okay, feel free to stop me.
demo进行时 我经常都在打字 A lot of time when I'm demo, I'm typing away,
没注意到你们举手 你们可以大声叫出来 I'm not seeing you raise your hand, so feel free to shout out.
这里我要创建一个新项目 So, I'm going to create a new project here, and I'm going
我要用单视图应用 to do single view application, even though I'm going
我要用Core Data 我说过你可以点这个 to use Core Data, and I told you that if you click on this one,
你可以看到一些生成的Core Data代码 you can see some generated Core Data code.
我并不打算使用这里生成的代码 I'm actually going to use the code that's generated here,
不过我会把它搁到应用委托的类别里 but I put it off into a category of my application delegates,
这样你就不需要真正去看它 so that you don't really need to look at it.
我会把它包含在demo中 你会看到 I'll include it in the demo, and you can see it.
不过 在作业中 But, for your homework, you're going
你们会用到UIManagedDocument来做Core Data的东西 to do your Core Data stuff using a document. UI-managed document,
我不打算展示这个 and I'm not going to show that.
因为我要你们 Because I want you to kind of go through
自己去经历和熟悉这个过程 the process of trying to figure that out for yourself.
我要创建一个和平常一样的普通单视图应用 So, I'm just going to create our normal single view application like we usually do.
名字称作Photomania I'm going to call is Photo Mania,
这是一个通用app 但我这里只演示iPhone版 and it's going to be universal app, although I'm only going to use the iPhone version.
我只打算演示这个应用的梗概 I'm only going to do the bare bones of this application.
这个应用的作用是查询Flickr 那些URL What this application is going to do, it's just going to query Flickr those URL,
最新地理参考照片的URL 同Shutterbug中一样 the URL for recent geo reference photo theme, the same thing we did for Shutterbug.
不过这里将不仅仅是展示照片 But instead of just showing the photos,
它还会展示这些照片的拍摄者清单 it's going to show you the list of photographers who took those photos.
在这个demo后 你可以很容易做成 And then, you know, after this demo, you could easily make it
点击拍照者 它就能展示这个拍照者的照片 so if you click on a photographer, it shows you the photos by that photographer.
点击照片 使用图像视图控制器展示给你一张图片 Click on the photo; use the image view controller to show you an image.
这就是我们要创建的应用 So, that's kind of the application we're trying to build.
我们不打算创建其它那些TableView We won't get all the way to building those other TableViews.
我们只打算着眼于主TableView 这就是它的作用了 We're just going to do this main TableView, but that's what this is going to do.
要展示那些拍照者 And, the way it's going to do that; the way it's going to show those photographers
策略就会同Shutterbug略有不同 is kind of a little bit different strategy than we saw with Shutterbug.
我打算在应用的后台 I'm going, in the background, basically of my application,
持续每隔一段时间查询Flickr 并获得更多照片 be constantly querying Flickr every once in awhile, and getting more and more photos,
然后把这些都丢到Core Data数据库中 and then just throwing them into a Core Data database.
把它们丢到后台 Okay, just throwing them in there in the background.
同时 我还会有TableView看着这些数据 Meanwhile, I'm going to have TableViews that are going to be looking at that data.
看着拍照者 点击他们 Looking at the photographers, clicking on them,
看着照片 looking at the photos,
而且这将一直自动更新 and that's just going to be always be updating automatically, all the time.
这就是我们所要创建的app Okay, so that's the app that we're going to build.
这里有些东西需要讲 So, there's some things to talk about here.
如何创建一个Core Data数据库 How to build a Core Data database.
如何使用FetchedResultsController关联它和TableView How to hook it up to a TableView with the Fetched Results Controller.
如何在后台进行取回 How to fetch things in the background.
今天我们会讲到所有这些 如果时间够的话 We're going to talk about all those things today if we have time.
好 起名叫作Photomania Alright, I'm going to call this Photo Mania.
把它放到通常的Developer里面 I'm put it the developer where I usually put things.
这里 Here it is.
我将直接创建我的数据模型 I'm going to dive right in with building my data model.
我的schema 这就是数据库世界的叫法 My schema, as you would call it in the database world.
这是对所有实体及所有这些东西的描述 This is a description of all the entities and all that stuff.
我们在幻灯片中也看过这怎么做 We saw how to do this all in the slide, so I'm going
我这里打算实况演示一下 to just going to show you what it looks like live.
要创建新schema 我点新文件 So, when I want to create a new schema, I do new file.
我要创建新文件 Okay, so I'm going to do new file.
到这里的Core Data 选择这个数据模型 I'm going to go up here to Core Data, and pick this data model,
不是映射模型 而是这里的数据模型 点它 not mapping model, data model right here, so click that.
叫什么名字都行 We can call it anything you want.
这个就用app名吧 Photomania I'm going to call this one the name of my app, photo mania,
问我要放到哪 and it's asking where do you want to put it, and I'm going
我要把它放到顶层 和其它东西一起 to put it at the top level here where all the rest of my stuff is.
这是我的控制器 委托等等 Here's my controller and delegates something like that,
我就把数据模型文件放这里 so I'll put this data modeling file there.
这就创建了数据模型文件 它是空的 And, so it creates this data modeling file. You can see it's empty.
没有任何实体 属性 取回属性这些 I've no entities or attributes or Fetch Properties or any of that business.
我们下面就来添加一些 So, let's just start adding some.
到下面这里 添加实体 So, I'm going to go down here to the bottom, add entity.
点这个 Click that.
加了一个 叫Entity It added one, called entity.
双击 改名为Photo I'm going to double click, and call it photo.
我需要照片 So, I need photo, and
我还需要显示拍照者 因此还要添加Photographer I display photographers too, so I better add photographer.
有一点非常重要 Now one thing that's really important
创建schema时 你需要把实体 when you make a schema is you want to put the entities
和属性放到这里 支持你所创建的UI and the attributes in there that support the kind of UI you're building.
这对于作业是非常重要的内容 Okay, and that's really important for you to understand in your homework.
我这次布置的作业很简单 Okay, this homework that I'm assigning you is pretty
前提是你要选一个好的schema straightforward, as long as you pick a good schema.
如果选择对了数据库中的实体和属性 If you pick the right entities and attributes in the database,
将信息表格就很容易搞定 it's just really easy to just throw up tables of information.
否则的话 如果schema都错了 If you don't, if you have kind of the wrong schema
或是schema太简单 or just too simplistic of a schema, you don't put
没有足够属性让UI正常运作 in a couple of attributes that you need to make your UI work,
你就会很难得到信息 it can be like ah, how do I get that information.
schema是你的奴隶 So, the schema is your slave,
你要创建好schema you get to make the schema however, you think it's going
最好地支持你创建的应用程序 to best support the application that you're building.
照片和拍照者 So, photo and photographer.
Photo有哪些属性呢 What kind of attributes does a photo have?
显然它有标题 还有副标题 Well, let's see, of course it has its title, and it has a subtitle.
这是来自Flickr的简述 That's that little description from Flickr.
它还有来自Flickr的图像URL It has the URL of its image from Flickr.
或许还有缩略图的URL Probably has the URL of its thumbnail as well.
实际上 我们还可以存储缩略图数据 In fact, we could even store the thumbnail data,
你们在作业中需要这样做 这里 which you're going to want to in your homework, right in here,
将数据放进来 不只是URL 还有一点很重要 put the data in, not just the URLs, and also importantly,
照片有一个标识符 来自Flickr photos have a unique identifier that comes from Flickr.
当我从Flickr获取数据时 我不希望… Because when I get data from Flickr, I don't want to be,
有时点Flickr 你会得到相同的照片 sometimes you click Flickr, you get the same photo again,
我不想把这个复制到我的Core Data数据库 and I don't want to be duplicating that in my Core Data database.
我要查看Flickr中的这个唯一标识符 So, I'm going to look at that unique ID from Flickr,
让这些在我的Core Data数据库中也成为唯一的 and make those unique in my Core Data database as well.
添加了这些后 我看到这里有个红色警告或错误 So, I've added these, but I've got to set their, you can tab warning here, or any error,
因为这些还没设 because I haven't set these.
点这个 你可以看到 If I click on this, you'll see.
必须定义类型 我们来设定类型 Must have a defined type, so we got to set a type for these.
这四个都是字符串 These all happen to be strings; all four of these,
不过可以设想 这里也可以有日期 but you could image putting things like dates,
例如作业中 你要有最近标签 like for your recent tab in your homework.
你会有最近浏览日期这些 You probably want to have some sort of last viewed date or something like that.
放到这里很合理 还有数字这些东西 That's perfectly reasonable to put in here, and numbers and all that stuff.
让我们这样做 So, we'll just do that.
Photographer更简单一些 Photographers are a little simpler.
它们只有name They just have a name.
拍照者只有名字 Okay, a photographer has a name.
这是字符串 That's a string.
就这了 That's about it.
我讲过 我们可以从图像上看这些实体 And, remember I told you we could look at these entities graphically
点下面这个小按钮就行了 with this little button down here.
这里 Here it is right here.
它们重叠到了一起 不过我可以选取移动 They're kind of smashed on top of each other, but I can pick them up and move them.
当我移动一个时 中间就会得到空间 And, you can see that as I move one, it kind of makes space,
如果在它们之间创建关系 alright, keep them, and if I have relationships between them,
这些关系也会在移动过程中得到保持 it will keep those relationships sensible as I move them around.
让我们创建一个关系 So, let's create a relationship.
照片和拍照者之间是什么关系 What is the relationship between a photo and photographer?
幻灯片上我就讲过 whoTook 这里我control拖动 As we saw on the slides, who took, basically, so I'm just control dragging.
按住control键 从照片到拍照者拖动 I'm holding down control right now, and dragging from photo to photographer,
这样新关系就得以创建了 and I have this new relationship created.
如果移动这些东西 And again, if I move these things around,
这个会保持不变 this things will stay with,
下面给这些关系命名 so let's go ahead and give these relationships names.
在照片一侧 这是whoTook关系 On the photo side, this is the who took relationship,
在拍照者一侧 这是photos 我们可以在检查器中查看 and on the photographer side, this is the photos, and we can inspect this right here
查看器在这里 with this little guy right here; the inspector.
我们可以看到属性中的各种东西 And, we can see all kinds of things about our particular properties.
对属性和关系都是如此 This is true for properties, and also for relationships.
当然 我们知道 And, of course, we know
这是一个To Many关系 that this is a to many relationship, right?
因为拍照者可以拍多张照片 这就得到了双箭头 Because a photographer can have many photos, so we get this double arrow here.
不过这里是To One关系 因为一张照片只有一个拍照者 But, it's a to one relationship here, because a photo, only one guy took the photo.
移动这些 And so, if you move these things around,
所有这些关系仍会保持 it will keep this all kind of looking okay.
这里 我们就设置好了我们需要的一切 Alright? So, now, we've kind of set up everything here that we need.
这是一个很简单的应用 我们不再需要任何的属性 This is a pretty simple application, so we don't need any more attributes.
不过在你的应用中 你至少还要有 But, in your application, you're going to have at least one
一两个实体和一些属性 or more two more entities and some attributes on there,
照片上也需要更多一些属性 and some more attributes on photo.
我留了一些工作给你们做 So, I left with some work to do.
有了这个之后 我们希望能够访问这些东西 Okay, so now that we have this, we want to be able to access all this stuff
在Objective-C代码中 使用属性和常规类句法 in our objective C code using properties and normal class syntax.
我们要做我讲过的这个 So, we're going to do this thing we talked about,
也就是 生成ManagedObject的子类 which is generating manage object subclasses.
我选择我要的类 So, I'm going to pick the classes that I want.
到这里 为Photomania创建NSManagedObject的子类 I'm going to go here and say create NS managed objects subclass for photo mania,
这里同时创建Photo和Photographer and we'll do photo and photographer, both of them.
点这里 放到你想放的地方 And, we click this, says where do you want them?
也都放到顶层 I'll put those also at the top.
这样所有东西就都在顶层了 I'm going to put everything at the top level here.
这里 我有Photo和Photographer And here, I have my photo and photographer,
Photo看起来很好 and I can see that photo looks okay.
它有Photographer whoTook It's got photographer who took,
但Photographer就没这么好了 but photographer, not so much, right?
它只有NSManagedObject对应removePhotosObject It's got NS manage object for remove photos object.
这里应该是Photo This should really be photo star,
我打算再次生成这些东西 so I'm just going to generate these things again.
实际上这很蹩脚 Okay. And really, it's too bad
它不能一次给我把两次生成给办了 that it doesn't automatically do this two pass generation,
不过手动就行了 but you end up doing it yourself.
务必习惯这种生成 因为你会经常用到 Get used to doing this generate, because you're going to do it a lot.
你会经常添加实体 You're going to be constantly adding entities,
改变属性 等等 changing attributes, like that, and you're just going
你将经常再生成这些东西 to constantly be regenerating these things, and it's going
你会被要求替换它们 to ask you to replace them, and so
不要不习惯这样再生成 don't be uncomfortable about doing that regenerate.
这是很常用的东西 现在我们就胜利了 It's a common thing to want to do, and so now we're winning here.
我们有了这些很好的类 So we have these nice classes,
现在我们可以用点符号来访问它们所有的属性 and now we can use properties to access all their attitudes.
例如用photo.title来获得标题 Photo dot title to get at the title for example.
不过我之前就讲过 我们想要添加代码 But, as I said before, we might want to add code, and I do want
例如这里是给Photo加 to add code to photo here.
我想添加创建照片的代码 I want to add code to create a photo.
让它能够添加 Okay. To make so that I can add,
插入一个Photo对象到数据库中 insert a photo object into the database.
我将使用category来做这个 And so, I'm going to do that using categories.
category也就是我刚讲的新Objective-C特性 Remember categories is that new objective C thing I was telling
记得吗 我们可以往一个类中添加代码 you about, where we can add code to a class
又不创建它的子类 这里我打算把代码 without sub-classing it, so I'm going to add code
加到Photo类 而不创建Photo的子类 to this photo class, right here, without sub-classing photo.
做法是文件 新文件 Okay, so I do that with file, new file,
这里不再选择Objective-C类 and instead of picking objective C class,
而是选择Objective-C category I'm going to pick objective C category.
这时 它会问我要创建category的是哪个类 And when I do, it's going to say, what class do you want to make a category on,
要创建category的是Photo类 I'm going to make a category on the class photo,
我打算叫它Flickr and I'm going to call it Flickr
因为我要给Photo加的这些方法 because that's what this method that I'm, these methods I'm going to add to photo
都是将Flickr同数据库关联起来 are all kind of hooking Flickr up to the database,
因此 我认为Flickr用作这个category名很好 so Flickr seems like a good name for that category.
它还问我要放在哪里 And, it wants to know where I'm going to put them.
放在和其它东西一样的位置 Put them in the same places everywhere as everything,
这是我的.h 这是我的.m and so here's my .H and here's my .M,
它问我想要做什么 and it's asking me what do you want to do here.
这里我要实现的方法 So, the method that I want to implement here is a method
是取一个Flickr字典 that essentially takes a Flickr dictionary
将一个照片对象加到数据库 and adds a photo object to the database,
并返回一个指向它的指针 and returns the pointer to it to me.
都知道我这里要做什么吗 Everyone understand what I'm going to do here?
我打算把这个叫作 Photo… 确定选的是相同名称 So, I'm going to call this thing, photo, make sure I pick the same name,
免得后面出现问题 so that I don't get sidetracked here.
photoWithFlickrInfo 这将是一个NSDictionary Photo with Flickr info and this is going be NS Dictionary,
照片字典 除了这个照片字典 photo dictionary, and what else do I need beside
我还需要什么才能在数据库中创建一个对象 that photo dictionary to create an object in the database?
同数据库的关联 A hook to the database.
我需要知道 我要把这张照片加到哪个数据库中 Okay, I got to know which database you want me to add this photo to.
因此 我还需要 inManagedObjectContext So, I also need in managed object context,
NSManagedObjectContext context NS managed object managed context context.
我只需要这些 Okay, and that's all I need;
不过是Flickr信息和context just the Flickr information and the context.
这是你要我创建这张照片的地方 我会在这里创建 That's the place you want me to create this photo, and I will.
这里我还会有一个方法 And, I'm also going to have another one here; because I know
因为我知道Flickr照片会大批量下载 that I'm going to be downloading these Flickr photos in big bunches.
每次我调用URLGeoreferencePhotos Every time I call URL, geo reference photos
大概是叫这个吧 or whatever it's called,
它会给出很多 例如一两百个 I'm going to get a whole bunch of them, like a hundred or 200 of them.
我要有一个批量加载方法 我称其为 So, I'm going to have a bulk load one that I'm going to call
loadPhotosFromFlickrArray load photos from Flickr array,
NSArray NS-Array
photos photos
这是Flickr NSDictionary的 of Flickr NS dictionary
inManagedObjectContext into managed object context.
NSManagedObjectContext NS managed object context.
这就是批量加载方法 我会反复调用这个 Okay, so that's just going to bulk. It's going to call this basically repeatedly,
虽然这并不见得是最高效的做法 although that might not be the most efficient way to do that,
后面我们还会稍微讲到 and we'll talk about that in a little bit.
让我来实现这两个方法 So, let me implement these two methods.
这是category的接口 Okay, this is the interface of my category,
而这是category的实现 and so here's the implementation of my category.
我可以做任何我想做的事情来实现这些 Now, I can do whatever I want to implement these, except,
但我不能使用任何实例变量 I can't use any instance variables,
这里不能有任何属性 so I can't have any properties that are, you know, here,
我需要用photo来实现它们 这样应该就行了 so I have to implement them basically in terms of photo, if that makes sense that way.
这个会返回一张照片 因此我说 So, this is going to return a photo, so I'm going to say,
photo = nil photo equal nil,
下面这里则是return photo 而两者中间 and down here, return photo, and in between,
我需要在数据库中找这张照片 或是创建它 I got to go find this photo in the database, or create it, or
或是别的什么 whatever it may be.
我们首先来问数据库 你是否已经有了这张照片 So, let's start with asking the database, do you already have this photo?
如何问数据库是否已经有这张照片呢 And, so how am I going to ask the database if the photo is already there,
答案是 我将尝试取回它 and the answer is, I'm going to try and fetch it.
我将用一个FetchRequest So, I'm going to have a Fetch Request, and it's going
这是一个到照片表格中的FetchRequest to be a Fetch Request into the photo table, if you know anything about it, or into photos.
它会返回照片 因为我要找这张照片 It's going to return photos, okay, because I'm trying to find this photo.
然后 请求需要一个谓词 什么谓词呢 And then, the request needs a predicate, and what is the predicate.
我要找哪张照片 Which photo am I looking for?
我要找的照片 其唯一标识符unique要等于 Well, I'm looking for the photo whose unique equals the same
这个照片字典的unique unique that is photo dictionary right here,
让我们获取这个 so let's go get that.
NSString *unique = … NS-string star unique equals, and we're going
这里我们需要import FlickrFetcher.h to need our Flickr Fetcher import, Flickr Fetcher .H,
我将抓取FlickrFetcher.h so I'm going to grab Flickr Fetcher .H
从之前我们所做的 也就是Shutterbug from the thing we did before, which was Shutterbug.
这是Shutterbug 这是FlickrFetcher So, here's Shutterbug, and here's Flickr Fetcher.
我要把这整个都拖到这里 I'm just going to drag that whole thing right in here.
这样我就有了FlickrFetcher.h Alright, so now I have Flickr Fetcher .H,
而在FlickrFetcher.h内 and inside Flickr Fletcher .H,
我们可以获得照片的唯一标识符 we can get the photos unique ID
使用这个FLICKR_PHOTO_ID with this Flickr photo ID.
于是 我可以到这下面说 So, I'm going to go down here and say,
photoDictionary[FLICKR_PHOTO_ID] photo dictionary, Flickr photo ID.
这里我可能要用valueForKeyPath Now, I might want to do value for key path here,
防止这张Flickr照片ID中间有点这些 just in case this Flickr photo ID might have dots in it or whatever,
Flickr Flickr.
也许会有点 就像描述一样 You know, it might have dots in it like the description has.
这里我就这样 不过valueForKeyPath或许更好 Okay, I'll leave it this way here, but value for key might be a good idea.
这里我将尝试在数据库中 So, I'm basically going to fetch into the database to try
找到这张独一无二的照片 看是否已经存在 and find this unique photo; see if it's already there.
这里我需要一个NSError And, I do that by saying I need an NS error here,
我要说 NSArray *matches = [context … and then I'm going to say NS array matches equals context,
因为我们需要一个context来进行取回 because we always have to ask a context to do a fetch.
executeFetchRequest 那个请求 Execute Fetch Request, that request,
如果有错误 返回错误 and if there's an error, return an error.
这样我就有了这些照片 So, now I've got those photos.
这些照片 这里是一张匹配的照片 Those photos hopefully, in this case a matching photo or maybe not,
被取出来了 has been pulled out.
这个匹配可以有多种 Now, this matches can have a number
不同的状态 或者不同的值 of different states or different values.
这里有一点 它可能是nil One thing is, it might be nil.
如果它是nil 或者 如果这个错误不为nil If it's nil, okay, or if this error is not nil,
这里是或error let's say or error
或者 这里的另一个错误条件是 or another error condition here is
匹配计数大于1 that the matches, matches count is greater than one.
因为这些是唯一的 Okay, because these are supposed to be unique,
如果我这样做得到了多张照片 and so if I somehow got multiple photos by doing this,
这显然也是错误的 我需要处理这里的错误 that would also be an error condition, so I have to handle error here.
处理错误 Handle error.
今天我不打算做这个 不过你们可以想象的到 I'm not going to do that today, but you can imagine.
否则 如果匹配找到了东西 我们可以直接返回它 Okay, otherwise, if the matches found something, then we can just return it.
如何返回它呢 So, how do we return it?
这是一个数组 但这个数组有且只有一个匹配项 This is in array, but this array is going to have the one and only one match,
因此我可以说 匹配第一个对象 so I'm going to say matches first object.
你也可以说最后一个对象 Could also say last object.
否则 匹配计数如果是0 Otherwise, this matches dot count is 0,
返回的是空数组 so it returned an array, an empty array.
这意味着我要找的唯一标识符照片并不存在 That means I looked for that photo that had that unique,
这意味着我要找的唯一标识符照片并不存在 I couldn't find it, so that photo doesn't exist.
我现在需要创建它 I now need to create it.
记得如何在数据库中创建对象吗 Okay, everyone remember how to create an object in the database?
NSEntityDescription NS entity description,
insertNewObjectForEntityForName insert new object for entity for name,
我们插入一张照片 显然我们还需要指定context we're inserting a photo, and we obviously have to specify the context.
我们于是创建了一张照片 So, we created a photo.
很好 Excellent.
下面 我们进一步设置照片的属性 And, now, let's go ahead and set the attributes of the photo.
我还要设置照片的unique I'm also going to set the photo's unique, go to unique.
这里我设置了照片的标题 副标题 图像URL Alright, so here I'm setting the photos title, subtitle, image URL.
这里我用valueForKeyPath 设置了标题 So here I'm just setting the title using value for key path
从Flickr字典中 out of the Flickr dictionary,
副标题 给定描述下 还有URL the subtitle, given the description, the URL.
我把这个URL用于照片格式这些 I'm using this URL for photo format thing.
我要把它转换成字符串 因为数据库中不能放URL I have to turn it into a string, okay, because can't put URLs in the database,
但可以放字符串 but we can put strings there,
然后我还有拍照者名字 and then I also have the photographer name,
不过我有一个和拍照者实体之间的关系 but I have a relationship to photographer entity,
因此 现在我还需要创建一个拍照者 so now I need to create a photographer as well.
于是这里 从Flickr加载一张照片 So here, loading a photo, right from Flickr,
会导致拍照者也被创建出来 is causing a photographer also to be created.
这在作业中也会发生 Okay, and this is happen in your homework too in spades.
你会从Flickr下载这些照片 Okay, where you're going to download these photos from
你还要创建整个数据库 Flickr, and you're going to build your entire database.
很多实体总在被创建 Lots of entities are going to be created all the time,
每次有东西从Flickr回来都是如此 every time things come back from Flickr.
那我们如何创建拍照者呢 So, how do we create a photographer?
我会和这里做一样的事 Well, I'm going to do the same thing I did here,
这里照片的category创建的是这个 where I have this photo category that creates that,
我要对拍照者做相同的事 I'm going to do the same thing for photographers.
新文件 创建另一个category I'm going to say new file, another category.
这将是拍照者的category This is going to be a category in photography, photographer.
这不是特别用于Flickr的 This one's not Flickr specific,
因为拍照者只有一个名字 because photographer only has a name, so I'm going
因此 我打算把这个category叫作Create 而非Flickr to call this category, just to be different, create, instead of Flickr.
还是放到相同的地方 So, we'll put this in the same place.
这里 Here it is right here.
上面这里 Photographer 对 Put this up here, and Photographer, yeah.
它非常类似 It's very similar.
几乎完全一样 It looks almost exactly the same.
我们为它进行取回 We're going to fetch for it.
我们要处理找不到的情况 设定名称 等等 We'll handle, if we can't find it, set the name, all that business.
把这些放到头文件中 So, we put that in the header file, okay.
尝试加速一些 Just trying to speed it up a little bit here.
看我做这些 你们学不到新东西 You wouldn't learn anything new by my doing this all again.
这样我就能创建一个拍照者 给定名字 给定context But, now I have a way to create a photographer, given a name in a given context.
把那个用到这里 So, we'll use that over here,
我们说 photo.whoTook = … and we'll just say photo.whotook equals,
我们需要导入那个category and we've got to import that little category.
Photographer+Create Photographer create.
Photographer Photographer,
photographerWithName photographer with name
名字也就是我们从Flickr获得的拍照者名字 and the name is the photographer name that we got out of the Flickr thing,
context一样 and same context.
大家都理解了这个方法吧 Everyone understand this method right here.
对于这个方法 So, this method is going to,
如果我们从照片信息给它一个Flickr字典 if we give it a Flickr dictionary from photo info,
它会返给我们一个数据库中的照片对象 it's going to give us back a photo object in the database,
要么是创建一个 either by creating it
要么是找一个已经有的 or by finding one that's already there,
如果有问题 它也可以返回nil and it will return nil if it has a problem with it.
都理解了吗 Everybody got that.
好 这样 我们就有了很好的访问方式 Okay. So, now that we have this thing, we have these nice ways to access it.
下面再简单讲下TableView Let's talk a little bit about the TableView,
以及如何把这些东西显示在TableView中 and how we're going to display this stuff in a TableView,
我们能很简单地 and it's really a simple matter
创建一个新的TableView子类 of creating a new TableView subclass
来实现那个FetchedResultsController that implements that Fetch Results Controller business.
我讲过 你们有这个Core Data的东西 And I told you that you were going to have this Core Data thing
让这个更容易 我们现在要用它 to make that easy, so we're going to use that.
这是CoreDataTableViewController Here is the Core Data TableView controller.
拖进来 We'll drag it in.
这里我们来看看它 We'll take a look at it here.
这是Core Data表格视图 Alright, so here's the Core Data TableView.
这是它的头文件 Here's its header file.
可以看到 它有这个FetchedResultsController的东西 You can see, it just has this Fetch Results Controller thing.
它还有一个强制取回的方式 但你不需要这样做 It also has a way to force it to Fetch, but you don't ever have to do that.
它会自动发生 It will happen automatically.
它有这个属性 再看这个的实现 So, it has this property, and if you look at this implementation of this thing.
除了设置FetchedResultsController… Besides setting the Fetcher Results Controller,
我放到这里的其实主要是一些日志 which is mostly just a bunch of logs, okay, that I put in there.
它还实现UITableViewDataSource 看到了没 It's implementing UI-TableView data source, you see,
用FetchedResultsController in terms of the Fetch Result Controller.
然后 它还做了这些委托的事情 And then, it's also doing this delegate business,
监视数据库中的变化 where it's watching for changes in the database.
就这些了 Okay, and that's it. That's all it does.
这段代码实际上复制粘贴自 And this code is actually copied and pasted
NSFetchedResultsController的说明文档 from the documentation for NS Fetch Results Controller,
这里没有什么值得惊喜的 so this is nothing exciting in here.
创建关联数据库的TableView时 So, when we create a TableView that wants to look in the database,
我们创建了类 okay, we've created objectives in class,
现在我们要创建CoreDataTableViewController的子类 we're going to make it be a subclass of Core Data TableView Controller.
我要创建的这个 So, I'm going to create one, and I'm going
叫作PhotographersCDTVC to call it photographers, I'm going to call it CDTVC,
CoreDataTableViewController的首字母 Core Data TableView Controller.
有些人很喜欢这种命名惯例 Okay, it's kind of a naming convention some people
我打算这样命名 like to use, and so I'm going to do that.
因为它会显示拍照者 这就是它的作用 Because it displays photographer; that's what it does.
创建这个 So, let's create that.
还是放到顶层 同其它那些一样 Let's put it top level; same place as everywhere else.
好了 创建成功 Here it is. It's created it.
这些东西我都不需要 I don't need any of this business for that.
我们来想想它的公共API And, let's think about its public API.
这个东西需要什么 What does this thing need?
它和数据库中很多其它东西需要的一样 Well, it needs what most things need that are doing database stuff.
它需要一个NSManagedObjectContext It needs an NS managed object context,
因此这个类 and so, this class,
它的作用就是 给你展示所有拍照者 its job in life is going to be, it will show you all the photographers
在给定context下 在给定数据库中 in a given context, the given database.
你到数据库中 You give it a point or two databases;
它会给你其中所有的拍照者 it will look in there and show you all the photographers in it.
这就是它的作用 That's what this thing is going to do.
要让这个发生 它只需要设置 So, to make that happen, all it needs to do is set
FetchedResultsController那些 在它的超类中 that Fetch Results Controller thing in its super class, right?
也就是CoreDataTableViewController 我来做这个 Which is its Core Data TableView controller, so I'm going to do that.
只要有人设置了ManagedObjectContext As soon as someone sets the managed object context.
这个叫作managedObjectContext吧 这样更清楚一些 Let's call this managed object context, so it's a little clearer.
只要有人设置了ManagedObjectContext As soon as someone sets this managed object context,
我就能设置我的FetchedResultsController I'm going to be able to set up my Fetch Results Controller.
只有有了context才能设置FetchedResultsController I can't set up my Fetch Results Controller until I have the context.
有时 context通过这样的公共API给到我 Sometimes, the context comes to me via public API like this.
有些视图控制器会从其它对象获得它们的context Some view controllers will get their context from other objects.
最显著的是 作业中这一点要注意 Most notably, and pay attention here for your homework,
如果有人给你一个ManagedObject 例如照片 if someone gives you a managed object like a photo
或拍照者 or a photographer,
你现在就有了context 因为NSManagedObject有一个方法 you now have the context, because NS managed object has a method
叫作managedObjectContext in it called managed object context.
它会给你对象的context It will give you the context that that object came out of.
了解这个很重要 So, it's really important to understand.
如果有人给你一个Photo 你就有了ManagedObjectContext If someone gives you a photo, you have a managed object context,
不过这个处在顶层 but this thing is at the top level.
它展示所有的拍照者 It's showing all the photographers.
我们还没从数据库中得到任何东西 We don't have anything yet out of the database,
因此有人需要告诉我们context so someone has to tell us the context.
从哪个数据库取回这些东西 Which database to fetch these things out of?
下面我只需要说 So, now I just need to say,
fetchResultsController = 什么东西 Fetch Results Controller equals something, and so I'm going
我要创建一个新的FetchResultsController to create a new Fetch Results Controller.
alloc init Alloc init,
init有所有这些参数 and its init has all these arguments here.
它需要一个FetchRequest It needs a Fetch Request, okay.
这里不要弄得这么晦涩 Actually, let's, so we don't get this kind of blackiness,
先来看这些参数 let's go ahead and make all the arguments first.
它需要一个FetchRequest It needs a Fetch Request,
因为这就是它的作用 关联一个FetchRequest because that's what it does is hook up a Fetch Request
那么 这就是一个请求 to something, and so, this is a request
请求的是拍照者表格 我们想要一个拍照者 into the photographer table, right, so we want photographer.
我们要展示所有的拍照者 We're going to show all the photographers.
这个的谓词是nil The predicate for this thing is nil.
谓词为nil是什么意思 What does that mean predicate nil?
谓词为nil是说 所有都要 Predicate nil means all of them.
如果你说谓词为nil So, if you say predicate nil,
这是说 我要所有的拍照者 that means give me all the photographers.
排序描述器 没错 我们要排序 And sort descriptor, yea, let's sort these things.
排序这样来 sortDescriptorWithKey Let's sort them by, let's see, sort descriptor with key, well,
我们根据拍照者名字来排序 let's sort photographers by their name.
实际上很有趣的是 你还可以通过关系来排序 Amazingly, you can actually sort things by things through relationships.
你还可以通过其它对象的属性来排序 So, you can sort it be other objects properties if you want.
不过这里 拍照者的排序 But here, photographer, we're going to have it sort
我们打算根据名字来 采用降序 by its name, and yes, we're going to have it descending,
这里的选择器我们选用localizedStandardCompare and we're going to use the selector here called localized standard compare, okay.
对于字符串 我们一般都用这个 Which is what we use mostly for strings that are going to appear when these interface.
数组要有闭括号 We need to close our array there,
因为这是一个排序描述器的数组 because this is an array of sort descriptors. We only need one,
我们还可以施加限制 例如只要一百个 and we could limit like, for example we could say, only give us a hundred.
我们只要一百个拍照者 这当然有些傻 We only want to see a hundred photographers, which would be kind of silly
因为这里是字母顺序排序 because we are sorting alphabetically,
这没有意义 这样的话 so this would not make sense, because we wouldn't get
名字字母序靠后的人就永远不会出现 to see the people whose names unfortunately happen to be end
因此这里我们不会这样做 of the alphabet, but we wouldn't do it here.
不过有些情况下需要这样 But, in other cases you would.
作业中 你可能就会这样做 In your homework, you very well might do that.
好 我们有了请求 有了ManagedObjectContext Alright, so now, we have the request, we have the managed object context.
这是这个方法的参数 我们也设置了 That's an argument to this method is what we're setting actually.
这是section那些 Here's the section thing.
我们可以在这里做 We could make sections here,
不过Photographer中没有东西可以做到这个 but there's really nothing in a photographer to do that,
因此这里我们不打算做 缓存也免了 so we won't do that, and we're not going to cache.
就是这样了 就只需要这些了 So, that's it. That's really all that's required
这样TableView就能工作了 不过还有一点 to make this TableView work, except for one thing,
也就是 FetchResultsController which is that Fetch Results Controller,
我讲过 它实现了所有的UITableViewDataSource I told you it implemented all the UI-TableView data source
不过有一个它不能实现 也就是 things, but there's one of them it can't implement which is
cellForRowAtIndexPath cell for row at index path.
它不知道对象的什么属性 Okay, it doesn't really know what attributes
你想放到UITableViewCell的哪些部分 of the object you want to put in which parts of the UI-TableView cell.
标题 副标题 它不知道这些 The title, the subtitle, it doesn't know those things.
因此 我们需要自己来实现 So, we have to implement that ourselves.
这是UITableViewCell So, that's UI-TableView cell,
cellForRowAtIndexPath cellForRowAtIndexPath.
这个东西你应该非常熟悉才行 You should be very, very, very familiar with this thing.
它是这样的 cell = It just looks like this, cell equal,
self.tableView Dequeue… self.tableview DQ,
这些是拍照者 因此这里称作Photographer Cell and well, these are photographers, so we'll call this photographer cells.
你们要监督我的工作 We got to make sure you guys are going to keep me honest.
确保我记得在故事板中设置这个 Make sure I remember to set that in the story board
确保我记得在故事板中设置这个 when we create one of these in the story board.
现在 我需要这个行和这个section处的拍照者 And now, I need the photographer that is at this row and section.
我将首先导入Photographer.h So, I'm going to import photographer first of all, not create, .H.
好 这就是Photographer Okay, so there's the photographer.
然后我要说 photographer = … And, then I'm going to say photographer equals,
这个怎么做呢 photographer = self. and how do I do this, photographer equals self.
fetchedResultsController objectAtIndexPath:indexPath Fetched results controller, object at index path, index path.
这就得到了这一行处的拍照者 Now I have the photographer that's in this row.
有了这个之后 So, now that I have that, I can do things
我就可以说 textLabel.text = 拍照者的名字 like text label.text equals let's say the photographers name of course.
再来点酷的 Also, how about something cool like this,
cell.detailTextLabel = 一个带格式的字符串 cell.detail text label equals a string with format.
如果我想要拍照者所拍的照片数量呢 What if I wanted to put how many photos this photographer has taken,
这个信息很容易从数据库中获得 well that information is readily available to me in the database,
%d photos percent D photos,
photographer.photos count photographer.photos count.
好 Xcode中 它是这样的 Okay, X code, shoot, the way it does that.
好 Okay.
我刚搞定了这个东西 So, I just go through that thing that goes through that thing
whoTook photos关系 that who took photos relationship.
我抓取了photos一侧 I just grab the photos side.
它是一个NSSet It's an NS set.
NSSet实现count计数 NS set implements count.
我就得到了我要的 I got what I need.
然后返回cell And, then let's return this cell.
大家都理解这个cellForRowAtIndexPath了吗 Okay, every one understand this cellForRowAtIndexPath.
有问题 Question?
对 这个属性 fetchedResultsController Yes, this property fetchedResultsController
继承自CoreDataTableView is inherited from Core Data TableView.
问得很好 That's actually a good question.
好 我们继续 Okay, so let's go ahead
来创建相关的故事板 毕竟我们现在在做这个 and build our story board while we're at this, and while we have this fresh in our minds.
它是这样的 So, here it is.
这是我们得到的默认情形 This is the kind of default one that we got.
删掉它 我们不需要它 Let's get rid this. We don't want this.
拖出一个TableView Let's go bring out a TableView. So, here I'm just going to drag a TableView out here.
将它设置为PhotographersCDTVC I'm going to set it to be a photographers Core Data TableView Controller, right,
然后按需求设置cell and then let's set up our cell the way we want.
我们需要它是带有标题的 因为它将拥有拍照者名字 We want it to be subtitled, because it's going to have the photographer name
然后是他们拍了多少张照片 and then how many photos they took.
我们需要Photographer Cell作为再利用标识符 We need to make sure that its photographer cell is our reuse identifier.
这就做好准备了 Otherwise, we're ready to go.
如果数据库中有东西 So, if we had stuff in our database,
现在开始运行 这个将能工作 and we hit run right now, this would work.
我们只需要这些 That's all we needed to do.
很少量的代码 将TableView和Core Data关联了起来 An incredibly small amount of code to hook up a table view to Core Data.
不过 数据库中还没有数据 But, of course, we have no data in our databases.
代码中还没有从Flickr查询加载任何东西 Nowhere in our code are we querying from Flickr or loading stuff in, and so
我将利用需要这个的机会 I'm going to use the opportunity of our needing that
为你们首次介绍应用委托 to introduce you for the first time to your application delegate.
我总喜欢把这些文件移动到支持文件 So, I've always been moving these files down to supporting files.
不要看它们 Don't look at them, you know,
我要展示一下这里面有些什么 and so now I'm going show you a little bit what's in here.
应用委托所监视的是 So, your application delegate is kind of like,
应用程序中最高层发生的情况 it is watching what's going on at the highest level of your application.
它会看应用的启动 It sees your application has launched.
它会看应用退出活动 Okay. It sees that your application has resigned being the active application.
它会看应用进入后台 It sees that your application is entering the background.
我们知道在iOS中 一个app Okay, we know then iOS, the apps,
在你进入另一个app时并不会退出 when you go to another app, they don't quit, they just kind
它会进入后台运行 我们可以找到这个 of move into this background state, right, so we can find that.
你可以知道它什么时候进入前台 You can find it when you move back into the foreground.
应用是否成为活动应用 If you become the active application.
是否退出应用 这些都可以知道 If someone's quitting your application, you find that out as well.
所有这些它都能够弄清 So, it finds out all these things, and we're going to learn
以后我们还会学到 about those later in the quarter, so I'm going
现在先把它们都删掉 不过我们要看这个 to delete all those now, but we are going to look at this one.
这个是didFinishLaunchingWithOptions This one is did finish launching with option,
这里它告诉你 你的应用结束了启动 so here it's tell you your application finished launching.
这时 你就可以 This is a great time to do things
开始一些Flickr取回了 like kick off some Flickr fetching or something.
在应用委托中 如果我要在这时进行Flickr取回 Now, if I'm going to be doing my Flickr fetching here in my application delegate,
我就需要context I need the context.
在你们的作业中 Okay, now in your homework,
你们需要通过创建UIManagedDocument来做到 you're going to do that by creating a UI managed document.
不过这里 我有一小段代码 But, here, I have a little piece of code that I got
来自我告诉过你们的另一个模板 from that other template that I was telling you about
把它复制过来 that I'm going to bring in here.
像这样 它是一个category It looks like this. It's a little category,
它有这个方法 createMainQueueManagedObjectContext and it has this method, create main queue managed object context.
这是一个ManagedObjectContext So, that's a managed object context
连有一个数据库 这个app的数据库 that attaches to a database, the database for this app.
这个只有一个 很幸运 而且是在主队列上 This only has one luckily, and gives me; it's on the main queue,
就像UIDocument一样 因此代码会非常类似 just like a UI document one is, so this code can be similar.
然后 我还可以保存 And then also, I can save it. Now,
UIManagedDocument并不需要这个saveContext I don't need this save context message for UI managed document,
因为它有自动保存 because it auto saves.
但这里 如果创建ManagedObjectContext时 But, here if I create this manage object context not using
不使用Document 那就需要这样做了 a document, then I have to do that.
我说了 这是另外一种做法 So, this is, like I said, the other way to do it,
这不需要用到作业中 我只是希望展示给你们 which you're not going to do for your homework, but I just wanted to show you,
我不打算展示UIManagedDocument I didn't really didn't want to show you the UI managed document,
因为我要你们自己去弄清楚 because I wanted you to figure that out on your own,
因此 我在演示中用这个 so we're going to manage this one.
好 我添加了这个 Okay, so I've added this.
这里是PhotomaniaAppDelegate This is a, you can see that the photo mania app delegate
category category,
它将这两个方法添加到了我的app委托 so it simply added these two methods to my app delegate.
下面 我将首先创建一些属性 Now, what I'm going to do here is I am just going to,
下面 我将首先创建一些属性 first of all, let me create some property.
这是我刚创建的一些属性 So, this is some stuff that I just created, some properties
这是我刚创建的一些属性 that I need and stuff like that.
先直接放到这里 使用的时候 For time, I'm just going to put them in here, but as we use them,
我会提到它们 I will refer to them.
这里我要用的一个 But one of them I'm going to use right here
是这个photoDatabaseContext is this photo database context.
我要在app委托中记录一个属性 So, I'm going to keep a property in my app delegate,
也就是我们要进行取回和读取操作的context which is the context that we're going to be fetching into
也就是我们要进行取回和读取操作的context and that we're going to be reading out of.
我打算在这里设置 So, I'm going to set that right here,
photoDatabaseContext = self create … photo data with context equals self create, oops,
我要导入PhotomaniaAppDelegate+MOC got to import that, import photo mania MOC.
这里我导入的是category的头文件 其中有create I'm just importing the category header file that has that create
也就是createMainQueueManagedObjectContext in that, so create main queue managed object context.
你也需要设置这个 So, again, you're going to set this as well, but you're going
不过是从UIManagedDocument进行设置 to set it from your UI managed document.
我有一个context So, now, I have a context,
我想开始进行Flickr取回 and I want to start doing some Flickr Fetches, so I'm going
我要调用另一个方法 start… 叫什么来着 to call another method here, start, what did I call this,
应该是startFlickrFetch start Flickr fetch, I think.
这就会在启动时 立刻开始Flickr取回 So, that's just going to fire off a Flickr fetch as soon as we launch.
我们刚启动程序 Okay, we just launched.
这个消息发送给我们 Get this message gets sent to us when we launch,
然后我将立刻开始Flickr取回 and I'm just going to fire off a Flickr fetch.
这就是启动时立刻开始取回的一种情况 So, that's one of the times we're going to fetch is as soon as we launch.
其它情况我们有时也会立刻开始取回 We're going to fetch some other times, so we're going to fetch as soon as we launch.
我打算把这个放到这里 So, I'm going to go ahead and put this in here
我们可以看一下 我不打算一行行输入 so we can look it, instead of my typing it in line by line,
这里有一些东西 and this is something where it's got some stuff
我在课上没有详细讲过 that I didn't teach really in detail in lecture,
不过你们应该基本能理解 but you should mostly be able to understand.
这里做的主要是创建了一个session Mostly what this is doing here is it creates a session,
你们知道URLSession 我创建了这个session so you know about URL sessions, so I'm creating this session.
这个session同我们之前创建的略有不同 This session is a little different than the one we created before,
因为它有一个委托 because look, it has a delegate.
看到了吗 You see that.
之前我们没有指定委托 We didn't specify a delegate before.
委托为nil Okay, we had delegate nil.
为什么要委托呢 And, why do we want a delegate, and why are we making it so.
因为我们希望后台开始的这些Flickr取回 Well, that's because we want these Flickr Fetches
因为我们希望后台开始的这些Flickr取回 that we're starting off in the background.
如果它们在应用不再运行时结束 If they finish while our application is no longer running,
我们希望它启动应用并告知我们 we want it to launch our application and tell us.
或者 如果我们在后台 Or, if we're in the background,
我们不是用户正在使用的应用 we're not the application the user is using right now,
而这个URL Flickr返回了一些信息 and this URL Flickr comes back with some information,
我们希望它唤醒一些 并让我们处理它 we want it to you know, make us, wake us up a little bit and let us process it.
通过这个委托 Okay, so by doing this delegate,
同某些后台session一起 我们可以做到这些 along with a background session, we can do that.
因此 我打算讲讲这个委托实现 So, I'm going to show you this delegate implementation.
我创建了这个session So, I create this session, and then I'm doing here
然后startFlickrFetch创建了一个任务 然后重新开始 on start Flickr Fetch is creating a task and resuming it.
这同我们之前做的完全一样 So, this is the exact same thing that we did before, right?
我们有一个URLSession We had a URL session.
我们创建一个下载任务 We created a download task.
唯一的差别在于 上次我们用到了competionHandler The only difference is last time we did with completion handler,
我们还有一段代码发生在下载回来时 and we had a little block that would happen when the download came back.
而现在 委托将会在URL加载返回时被调用 Now instead, our delegate's going to get called
而现在 委托将会在URL加载返回时被调用 when the URL gets loaded and comes back.
大家都理解了 Does everyone understand the difference
之前和现在的差别了吗 between what we did before and what we're doing here?
没有competionHandler Okay, no completion handler.
这里我们用的是委托 Here, we're going to use a delegate.
我们看看这个委托是怎样的 So, let's look at what that delegate looks like.
它是这样的 It looks like this.
其中有三个方法 Okay, there are three methods in it.
这三个方法 These three methods right here.
一是文件刚完成下载时 在这里 One is your file just finished downloading, and here it is
这里说是本地文件 as a local file, so this looks a lot
这和competionHandler很像 like that completion handler,
然后这里有一些我们不打算用 and then, here's a couple that we're not going to use, like giving you,
这个给了你一些进度 已经读了多少字节 this gives you some progress, how many bytes its read so far,
是否分块来读 if it does it in chunks,
然后 下载session也可以被打断 然后继续 and this also, download sessions can get interrupted and then resumed.
这个我们显然不打算细讲 无论如何 We're definitely not going to talk about that, but anyway.
可以看到这里我有一个错误 怎么做呢 Now, you can see I have an error here, okay, so what do we do.
这就是我们在competionHandler中做的 So, this is what we would have done in our completion handler.
而这里我们要做的 And, all we're going to do here is we are going
是获得context to get the context,
我们在程序已经完成启动方法里面 our little photo database context that we set up there
设置的照片数据库context in application did finish launching.
我们打算从Flickr下载这些照片 We are going to download those photos from Flickr,
Flickr照片是这个 你们很熟悉这个 对吧 so Flickr photos is this, which you're very used to, right?
dataWithContentsOfURL Data with contents of URL,
JSON 去JSON化 JSON, de-JSONize it,
从中抓取结果 grab the results out of that.
我们打算做这些 Okay. So, we're going to do that little thing.
这需要发生 这是一个本地URL This needs to be happening, you know, this is a local URL,
它可以发生在主线程 没问题 so it can be happening on the main thread, no problem.
下面 我们要在这个context中做点什么 Now, we're going to do something in this context,
注意 我用了performBlock and notice I'm doing perform block.
我在照片创建那个里面没有使用performBlock I didn't do perform block over in the photo creating one,
因为这是隐含的 我在创建一张照片 because it's kind of implicit. I'm creating a photo.
很显然 我在那个context中那样做了 Of course, I'm doing it on that context,
无论谁调用了那个 都希望在performBlock中做 so whoever calls that would want to do that in perform block,
这就是我这里所做的 and that's, in fact, what I'm doing here.
我调用了我们刚写的方法 loadPhotosWithFlickrData I'm calling that method we just wrote, load photos with Flickr data, okay.
我们还没有写这个方法 看看它 We didn't actually write that method, so let's look at that.
它在这里 So, that's over here.
我们有photoWithFlickrInfo 而这里是加载照片 Okay, we have photo with Flickr info, and here's loading the photos.
如何加载照片呢 So, how do we load the photos?
我只需要说 for (NSDictionary *photo in photos) I'm just going to say, for NS dictionary photo in photos,
一个个地给我每一张Flickr照片 okay, so give me each Flickr photo one by one,
然后self photoWithFlickrInfo: and then self photo with Flickr info,
photo inManagedObjectContext:context photo in manage context, context.
这其实是一种很低效的方式 Now, this turns out to be really an inefficient way
来加载这一百张Flickr照片 of loading those hundred Flickr photos,
因为每次调用这个时 我都在进行取回操作 because every time I call this, I am doing this Fetch.
如果我想加载一百张照片 So, if I want to load a hundred photos,
我需要一百次取回看是否唯一 I got to do a hundred fetches to see if those are unique.
这个显然有更好的做法 Okay, there are much better ways to do this,
一个额外加分的条件就是 and one of your extra credit items is for you to try
如果你能找到更好的做法 and figure out a better way to do this.
一种方式是看所有一百张照片的唯一ID Okay, and one way is to look at the unique Id's of all hundred photos,
一次把它们都取回 看哪些在数据库中 fetch them all at once, and see which ones are in the database,
因为你可以取回一个它们的列表 because you can get a list of them back, right?
在其中的那些 然后你可以一个一个地 The ones that are in there, and then one by one, you can go
创建没有的那些 and create the ones that aren't there.
这是一种做法 Okay, so that's one way to do it.
不过demo中我就用这个简单的方式 Okay, but this cheap and simple way is good for a demo, okay.
回到app委托 So, back to our app delegate
现在我们有这个loadPhotosWithFlickrArray and now we have this load photos with Flickr array.
让我们导入这个头文件 Let's go ahead and import that header file, which I do, oh,
或许我该起个别的名字 maybe I called it something different there.
问题在哪 What's the problem?
看看 或许我该起个别的名字 Let's see, yeah, I probably called it something different.
起相同的名字 Call it the same thing.
这是我的委托 这里 Here's my delegate, here it is.
称它为[声音不清]Flickr There we go, I called it [inaudible] Flickr, right?
这将加载所有这些照片 So, that's going to load all these photos.
注意 这里我保存了我的context Notice, I'm saving my context here.
这里是一个方法 ManagedObjectContext被保存 Okay this is a method, manage object context was saved.
这是一个错误 我忽略它 This is an error, which I'm ignoring.
实际不需要做这个 Don't really need to do this
用于UIManagedDocument 不过也无伤大雅 for your UI managed document, but it doesn't hurt.
想这样也行 You can do it if you want.
这不会破坏任何东西 This would not break anything
如果你有一个context来自UIManagedDocument if you had a context from UI managed document,
但我的没有 but mine's not,
因此我这里明文要求在加载一些照片后进行保存 so that's why I'm explicitly saving here after I load some photos in.
好 这很酷 Okay, so that's cool.
再来看看我们的表格 So, let's go look at our tables.
我们表格的公共API是什么 What is our tables, public API,
ManagedObjectContext managed object context?
我们从没设置过 We never set that.
我们需要设置这个表格的ManagedObjectContext Okay. We need to set this tables managed object context.
我们什么时候做 怎么做呢 Okay. When and how are we going to do that?
我们有这个应用委托 We have this application delegate.
这个全局的东西 It's kind of this global thing,
它在管理这个全局数据库 and it is managing this global database, but it needs
不过这个东西需要同其他需要它的人进行通信 to be able to communicate this thing out to anyone who needs to use it,
做法如下 and the way we're going to that is,
任何设置照片数据库context的时候 whenever we set this photo database context, we are going
我们都将发布一个通知 广播站那些东西 to post a notification, the radio station thing.
这是你们第一次看到如何发布一个通知 So, this is the first time you're going to see how to post an notification,
而不只是观测监听 not just listen with that observer,
这里你要发布 我们来做这个 you're going to actually post it right here, so let's do that,
发布通知 你几乎100%的时候 and when you post a notification you almost always 100%
希望创建一个头文件 这里我来创建头文件 of the time, want to create a header file, so I'm going to create a header file.
到头文件 C和C++头文件 Just go here to header file, C, C++ header file,
头文件将包含通知名 and that header file is going to contain the name of the notification,
还有用户信息中那些东西的名称 用于通知 and also the name of anything that's in the user info, for the notification.
保证这是在正确的地方 So, let's go ahead and make sure it's in the right place.
我将把这称作PhotoDatabaseAvailability It is. I'm going to call this photo database availability,
这个通知就是关于这个的 because that's what this notification is about.
广播站 This radio station talks
讨论了照片数据库是否可用 about whether the photo database is available, so I'm going
于是我创建了这个头文件 在它中间 to create this header file, and inside this header file,
我要给出两个#定义 I'm just going to put 2 pound defines.
一个是PhotoDatabaseAvailabilityNotification One is photo database availability notification.
这是通知名 That's the name of the notification.
我把它叫作这个 I'm going to call it this.
什么都行 不过我把它叫作这个 I can call it anything I want, but I'm going to call it that,
然后 通知发出之后 and then also when the notification goes out,
我们会把context包含在广播站广播中 I'm going to include the context in the radio station broadcast,
获得通知的人也可以同时获得这个 so the person can just get it when they get the notification.
这个用PhotoDatabaseAvailabilityContext I'll do that with photo database availability context.
名字中使用的是Context And, I'll use the same, well, just say context.
叫什么其实都行 It can be anything you want it to.
这是通知名 Okay, so this is going to be the name of the notification.
这是同通知一同的信息字典中的键 This is going to be the key into the dictionary of information
这是同通知一同的信息字典中的键 that comes along with the notification, alright?
下面 发送这个通知 So, now, let's send that notification, and I'm going
每次设置这个照片数据库context时 我都会这样做 to do that every time we set this photo database context.
有问题 Question?
哦 或许吧 Oh, yeah, probably.
提得很好 Good point.
对 这里没有等于 Yeah, no equals there.
谢谢 很好 Thanks, very good.
这里只不过是#定义 This is just a pound sign defined.
这是常数 我们不应该这样做 If it was a constant, we wouldn't do that.
好 让我们来设置这个照片数据库context Okay, alright, so let's set this photo database context,
在set… 这个应该有 so in the set; I think I might have a thing for that actually.
setPhotoData… Set photo data.
确实有 Yeah, I do.
设置这个照片数据库context时 So, here when we set this photo database context,
我们会公布这个通知 we are going to post this notification.
做法是使用NSNotificationCenter defaultCenter And we do that using the NS Notification Center default center,
postNotificationName post notification name.
我们指定名称 也就是这个 We just specify the name, which is this.
要想让这个正常工作 我们需要导入 For this to work, we need to import
PhotoDatabaseAvailability头文件 that photo availability, photo database availability header.
我们要发布这个通知 So, we're just going to post this notification,
我们同时还会给出这个用户信息 and we're going to give this user info along with it,
用户信息只是一个字典 它有一个键 and user info, it's just a dictionary that has one key,
也就是那个context which is that context,
而键的值是context and the value of that key is the context.
这就是广播站模型中发布的方式 So, this is how you post in the radio station model.
有问题吗 Any questions about that?
下面我要讲监听到广播站 And now, I'm going to show you listening to a radio station
而不用创建另一个方法 without having to create another method and all this stuff,
只是很简单的监听 谁需要监听这个 basically really straightforward listening, and who needs to listen to this?
这个需要 这个拍照者的东西 它需要监听它 Well, this guy does, this photographer guy, he needs to listen to it.
这个唤醒之后 So, basically, as soon as this guy awakes,
我或许该用initWithStyle and I might do this init with style,
或是别的什么指定初始化器 or whatever the designated initializer is
用于TableView 我要在awakeFromNib总这样做 for TableView, but I'm just going to it in awakeFromNib
因为我们知道这个TableView出自于故事板 for here, because we know this TableView is coming out of the story board.
我要说 通知中心 I'm just going to say, notification center.
首先导入Availability那个 First let's import that availability thing.
我要做的是NSNotificationCenter defaultCenter So, I'm going to do NS Notification Center, default center.
看这个 Watch this.
addObserver Add observer.
通常我们会使用选择器这些 Normally, we do it with a selector and all this stuff.
我打算使用基于block的这个 I'm going to do a block-based one.
之前你们不知道block 所以我没展示 也就是这个 I couldn't show you this before, because you didn't know block, but it's this one.
addObserverForName 我们给出名字 Add observer for name, and we give the name,
也就是PhotoDatabaseAvailabilityNotification which is photo database availability notification.
对象是谁能发送 我将其设为任何人 The object is who can send it, and I'll let anyone send this to me.
这是app委托 不过也可以是任何人 It's going to be the app delegate, but it could be anybody.
队列 我打算在我现在正处在的队列上做 The queue, I'm going to do it on the queue that I'm on now.
这就是nil的意思 That's what nil means,
不过我也可以说 NSOperation主队列 but I could say NS Operation main queue,
或是其它某个队列 不过这里我将其设为nil or some other queue even, but I'm going to say nil,
也就是说执行这个时我现在所处的队列 which means whatever queue I'm on right now when this is being executed,
使用block using block,
我双击这里添加一个代码块 so I'm going to double click here to put a block.
这是末尾 其中有些什么呢 Okay, and then that's the end of that, and what's going to be in this block?
只有一行代码 This is one line of code.
self.managedObjectContext = 这个通知 Self.manage object context equals this notification,
它的用户信息 its user info,
PhotoDatabaseAvailabilityContext photo database context.
抱歉 这一行有点长 Okay. Sorry for the very, very long line there.
应该能够容纳进来 确实能 I think it will fit, yeah it will.
我获得了这个block So, I'm getting this block.
它在广播站广播时会被执行 It's executed whenever this radio station broadcasts,
因为加入了context的用户信息 现在我就有了context and because I put this user info of the context, now I have the context,
我只需设置我的ManagedObjectContext and I just set my own managed object context,
它就会加载这个表格 and voila, it's going to load this table.
这方面有问题吗 Questions about that?
这是在发布和接收上的通知 So, that's notifying both on posting and receiving.
现在它就应该能够工作了 Alright, so now hopefully it will work.
我们来看看 这就是了 Let's go take a look at this, and there it is.
它运行得非常快 Okay, now it went really fast there,
因为它上次进行过加载 because it actually did the load last time,
只是我们没有看到 数据库已经加载 but we just didn't see it, so the database was loaded.
我要展示加载是怎样的 So, I'm going to show it what it looked like to load up.
我如何把这个清掉呢 So, how do I clean this out?
如何把数据库清掉 Okay, how do I clean my database out?
知道这个很重要 看好了 You're really going to want to know this. So, really watch this.
如果你想清掉数据库 If you want to get rid of your database,
尤其是想改变其schema的时候 especially if you want to change its schema.
因为如果你要改变其schema 你会重启 Because if you change its schema, and then you relaunch,
它会说不兼容数据库类型 it's going to say incompatible database type.
如果你想清掉它 你只需按Home键 So, if you want to get rid of it, just press the home key.
我还建议先退出Xcode I also recommend quitting in X code first.
然后按Home键 按住图标 Then press the home key, hold down, press to hold,
你也可以在设备上这样做 you can do this on your device too, and then,
图标会抖动 you get these jigglies,
然后点X删除应用 and then press the X to delete that application.
这会删除整个应用 That will delete the entire application,
包括数据库 之后重新在Xcode中启动 including the database, and now, when I launch again in X code,
它最开始会是空白的 it is going to start up blank,
在后台进行Flickr取回 doing the Flickr Fetch in the background.
ManagedObjectContext填满数据后 When that manage object context is filled up with data,
它就会神奇地自动出现在TableView中 it magically automatically appears in the TableView.
这就是FetchedResultsController正在监视context That's because that Fetch Results Controller is watching the context.
有问题 Questions.
好 我们回头看看这个 Yeah, let's go back and look at that.
问题是 我在哪设置的用户信息 The question is where did I set this user info,
这一小段用户信息 我在哪设的 this little user info right here, where did I set that,
这是我发布通知的时候 这里 and that was when I posted the notification here
在app委托中 in my app delegate,
这是我在设置context时 在我启动时 which I did when I set the context, which I did when I launched.
我启动 Right, so I launch.
这是启动 Here's launching.
启动时 我创建了context When I launch, I create the context,
你会和UIManagedDocument一起创建 you're going to create it with UI managed document.
我在这里设置了它 I set it right here.
这是那个的setter This is the setter for that.
我设置了它 Okay, I set it.
然后我把它发布 对其他所有人可用 Then I post it to available to everybody else.
有问题 Question.
问题是我如何确保 Yeah, so the question is, how can I make sure
视图控制器存在 而且在即时监听这个 that the view controller exists, and is listening in time
毕竟应用委托发布会很早 to hear this, because the application delegate is posting
就在启动之后 this pretty early, right after launch.
它发送了这个通知 It's sending out this notification,
答案是 这就是我在awakeFromNib中做这个的原因 and the answer to that is that's why I do this in awake from nib,
处在视图控制器生命周期的很早时期 really early in the view controller's life cycle, right?
当它刚创建时 这样它就有最大的获得机会 When it's first created, so it has the maximum chance of getting it,
不过也有可能 but it's possible.
不大可能 不过有可能 It's unlikely, but it's possible
后面创建的视图控制器可能会适时需要它 that some view controller you might create later in time might need it,
这时 你可能需要另外一种机制 and in that case, you might need a different mechanism, or you might need
你可能需要再发布 或是往下传递 to repost it or you might need to pass it along.
很多时候 我们都不想用这个机制 Okay, a lot of times, we don't want to use this mechanism
将context给到视图控制器 to give the context to view controllers,
原因正是视图控制器还不存在 exactly because the view controller doesn't exist yet,
在这个availability发生之前 它甚至都没有 until when this availability happens, it's not even there.
还有其它方式能传递ManagedObjectContext So, there are other ways to pass you managed object context around, and you always want
那些总需要用到这样的做法之前 to use those before a way like this.
我讲这个对作业是个提示 Okay, and I mentioned that in the hints, so the homework is,
传递ManagedObjectContext pass the manage object context
给视图控制器 受控于FetchedResultsController to a view controller that's NS Fetch results controller controlled
以一种有意义的方式 in a sensible way in a way that makes sense.
如果你segue到它 传递给它 通过准备它 If you're segueing to it, pass it to it by preparing it, right?
你不会希望指望这里的这些东西 You wouldn't want to rely on something like this.
好 再来讨论更多地加载数据库 Okay. Alright, now let's talk about loading our database some more.
现在 我们只在启动时加载了数据库 Okay, right now, we only loaded our database on launch.
这是didFinishLaunchingWithOptions Here's the application that did finish loading with options.
我们加载了数据库 然后开始Flickr取回 这就完了 We load our database right here and start Flickr fetch that's it.
这还不是很好 Well, that's not very good,
这意味着我们永远就这一批数据 that just means we only get one batch of data ever,
我们需要一直退出重启来获得新数据 when we launch our app, we have to quit, or have to relaunch it all the time.
我们希望能够在后台加载这些Flickr信息 So, what about kind of loading this Flickr information
我们希望能够在后台加载这些Flickr信息 in the background, and we can do that,
不过有两种不同的后台条件需要考虑 but there's really two different background conditions to consider.
一个 我们的app处在后台 One is background, and our app is in the background.
换言之 用户当前并没有用我们的app In other words, the user is not currently using our app, okay.
他们用过 但现在程序在后台 They were using it, but now it's in the background.
这时如何取回呢 Okay, how do I fetch when that happens.
这时的取回受系统限制 Well, fetching there is kind of limited by the system.
这时系统不会让你疯狂地取回数据 The system is not going let you just go hog wild fetching things
毕竟你不是用户使用的app when you're not the app that the user is using,
不过有时 它会让你这样做 but it will let you do it sometimes.
系统有时可以将你从背景唤醒 And, the way you can get the system to kind of wake you up
并让你取回一些信息 and let you fetch sometimes in the background,
或许一天几次 every once in awhile, I don't know, a few times a day,
具体的没人知道 不过有时 它会使用后台取回 who knows what, but sometimes, it's using background fetching,
这是一个多任务处理API 这是iOS7的新特性 which is a multitasking API, new for iOS 7.
我将把这当作一个例子 So, I'm just going to show you this as an example of what some
告诉你们多任务处理API是怎样的 of the multitasking APIs look like.
打开这个的做法如下 编辑你的项目 Now, the way you turn this is on, is you edit your project.
这是我的项目 我点了上面这里 You see this is my project I clicked on up here,
然后到这个选项卡 Capabilities(能力) and now you're going to go to this tab right here, capabilities.
来到capabilities 这里有很多 So, I go to capabilities, and there's a lot of capabilities.
从这里就能感受到 我们对iOS的接触有多粗浅 This really kind of gives you a feeling of how we're only skimming the surface of iOS,
看看这里你可以做多少事情 when you look at all these things you can do.
无论如何 我们要的在这里 But, anyway, we're going to do this one right here,
后台模式 我要把这个打开 background modes, and I'm going to turn this on.
打开时 这里有各种后台模式 When I turn it on, there's all kinds of background modes,
这些在后台都可以做到 things that I can be allowed to do in the background.
也就是说 我的app当前没有被用户使用 That is to say when my app is not currently being used by the user,
我要的是下面这个 and I'm going to pick this one down here,
后台取回 background fetch,
如果我打开这个后台取回 and if I turn this on, okay, background fetch,
那么 我就会偶尔在系统的允许下 then I will occasionally by the system at its discretion,
发送一条消息给我的app委托 甚至启动去做这个 send a message to my app delegate, perhaps even launching me to do it.
发送的消息是什么呢 And what is that message that gets sent.
我展示一下 I'll show it to you right here.
它叫… 我们将键入它 It is called, we're going to type it in.
application… 你可以看看能从应用中接收的所有东西 Application, so you can look at all the things you can receive from your application.
这里有好多东西 There is just a billion of them here.
看到了吗 See them.
我们会谈到有些 不过肯定不是全部 So, we'll cover some of those, but not all of them,
总之 这里有很多 but there's quite a few of them in here.
这里我们要的是这个 But, the one you get when you do this is this one right here,
performFetchWithCompletionHandler perform fetch with completion handler, okay,
这个被发给你 它会说 好 so this gets sent to you, and it's basically saying, okay,
我给你一个在后台做事的机会 I'm giving you an opportunity to go do something in the background,
取回或是别的什么 a fetch or whatever you want to do.
通常是取回 不过这里想做什么都可以 Usually it's a fetch, but you can kind of do whatever you want here,
你在做这个时的唯一责任是 and the only responsibility you have in doing this is
完成时调用这个completionHandler to call this completion handler when you're done.
你只需要做这个就行了 That's the only thing you absolutely have to do.
如果你不及时调用这个 If you don't call that in a timely manner,
系统会对你产生厌烦 它会停止取回 the system will grow tired of you, and it will stop fetching.
会停止发送这条消息给你 It will stop sending you this message.
我们对这个的反应越及时 系统就越有可能 So, more timely we respond to this, the more likely it's going
许我们在后台取回 to let you fetch in the background.
这里我们怎么做呢 Okay, so what are we going to do here?
我们这里只需要开始一个Flickr取回 Well, it turns out, all we're going to do in here is start a Flickr fetch,
然后我们立刻调用这个CompletionHandler and then we're immediately going to call this completion handler.
这还没完 We're not done.
这个取回我们才开始 This fetch, we just started it.
它需要一会才能完成 但是 It's going to take awhile to come back later, but
只要这个后台取回的东西在继续就行了 we're done, as far as this background fetch thing is continued.
获得结果这些会在别的时候发生 Okay, getting the result and doing all that, that's going to happen at some other time,
但这里我们完成了 but here, we're done, and we have to,
这个CompletionHandler的参数 the argument to this completion handler
是一个UIBackgroundFetchResult is a UI background fetch result,
也就是 无论这里做了什么 改变你的UI which is whether whatever you did in here changed your UI.
因为如果它这样做了 我们就需要更新任务切换器 Because if it did, we need to update the little task switcher.
iOS7中任务切换器是什么知道吗 Okay, does everyone know what the task switcher is in iOS 7?
它是这样的 这就是任务切换器 It looks like this, okay, here's the task switcher.
它让你能观看各种app 看app有没有做任何事 It lets you go through and look at various apps, if my apps are doing anything here.
让我们启动一个app Let's launch an app.
启动Safari Let's launch safari here.
现在进入任务切换器 这是Safari Okay, so now I go into task switcher, and here's Safari.
它给我展示了Safari中显示的内容 See how it's showing me what's actually in Safari?
它展示的并不是现在Safari中的内容 It's not really showing me what's in Safari right now.
而是上次我离开Safari时显示的内容 It's kind of showing me Safari the last time I left Safari.
或者 如果我有这种后台取回 Or, if I get this background fetch
和这个完成处理器 and this completion handler,
我说UIBackgroundFetchNewData I say UI background fetch, new data,
这时 它就会为我重新绘制这个 then it would redraw that thing for me.
它会给我一个重新绘制的机会 It would give me a chance to redraw it.
但我没有任何新数据 But, I don't actually have any new data.
这里我没有数据 因为我还没有从这里获得数据 I have no data here, because I haven't gotten the data from this yet.
这个Flickr取回还没有返回 因此它什么都不会做 This Flickr fetch has not come back yet, so it's not going to do anything.
这很棒 So, this is great.
这让我偶尔能够进行后台取回 This lets me occasionally fire off a background fetch,
但当数据返回时 我该怎么做呢 but when the data comes back in, then what do I do.
如果我在后台 Well, if I'm in the background,
这些URL东西都不会被调用 none of these URL things are going to get called.
因为我在后台 Because I'm back-grounded.
这些不会被调用并不严格成立 Now, it's not strictly true that these won't get called,
因为如果URL在我在后台时返回 because what will happen is if the URL returns while I'm
我最开始是在后台中 in the background, so I started it in the background,
如果它返回 我会得到另一个application的东西 if it returns, I'll get another application thing called
叫application handleEventsForBackgroundURLSession application handle events for background URL session.
它也有一个完成处理器 和之前不同 And, it also has a completion handler, a little different one.
事情在后台中发生时 这个就会被调用 Okay, so this is called whenever things happen in the background.
这个有一点很有趣 Now, what's interesting about this one is,
这个里面我也不需要做任何事 I don't actually need to do anything in this one either.
因为如果我实现这个方法 Because if I implement this method,
那么下面这里的委托方法 就会自动被调用 then these delegate methods down here will automatically get called,
它们已经知道如何从Flickr解包数据 并放入我的数据库 and they already know how to unpackage the data from Flickr and put it in my database.
它们已经知道怎么做 不过我还有这个责任 They already know how to do all that, but I still do have this responsibility
对于这个的完成处理器 for the completion handler on this one too.
这个完成处理器的调用需要等到 Now, this completion handler, we really do want to wait to call
我们已经处理了这个返回自Flickr的URL until we've actually handled this URL coming back from Flickr.
这个在处理返回自Flickr的URL Okay, this is handling a URL coming back from Flickr.
现在我们要等待 So, now we're going to wait. So, I'm going to have to hold onto it.
我这里有一个实例变量 So, I'm going to. I have an instance variable here,
我要保持住这个完成处理器 where I'm going to hold onto this completion handler,
这里就是那个实例变量 and here's the instance variable right here.
它是void Okay. It's a void.
没有参数 只是属性 It takes no arguments, just properties.
顺便说下 所有的block 都是copy属性 All blocks by the way, want to be copy properties.
在保持处理时 block需要被复制到堆 Blocks want to be copied into the heap when you keep a handle to them.
这需要大家知道 Something to know.
无论如何 我要保持这个东西 So, anyway, all I need to do is keep this thing, and then I need
然后我要在完成时调用这个 to call this when I'm done,
下面有个方法是做这个的 so I have a method down here that I use to do that with.
叫作flickrDownloadTasksMightBeComplete It's called Flickr download tasks might be complete,
因为我可能有多个下载任务发生 because I might have multiple download tasks happening,
于是这里我获得所有Flickr下载session所做的任务 and so what I do here is, I get all the tasks
于是这里我获得所有Flickr下载session所做的任务 that my Flickr download session is doing,
这就是这个方法的作用 它也有一个block that's what this method does, and it has a little block
这个会被调用 因为它无法立刻执行 that gets called, because it can't do that immediately.
有时 它需要一些时间才能弄清 Sometimes it takes a little time to figure that out,
如果没有下载任务剩余 and then if there are no download tasks left, right?
![dowloadTasks count] Not download task count,
那么我就会调用这个完成处理器 then I'm going to call this completion handler.
这个Flickr下载后台完成处理器 说我完成了 This Flickr download background completion handler, and say that I'm done.
这时 我就完成了下载 Okay, so now I've finished downloading it,
我重画了我的UI 我还调用了完成处理器 I've redrawn my UI, and I'm calling the completion handler,
这个完成处理器没有UI无数据的东西 and this completion handler doesn't have the little UI no data thing,
因为它总假设你会更新你的UI because it always assumes that you're going to update your UI.
总是假设 So, it's always assuming.
它在你那样做时会总是重绘 It's always going to redraw when you do that.
好 要看这是怎么样的 Okay. So, let's see what this, okay, to see what this is going
有一个很酷的方式 to look like, there's a really cool way.
我们这里分两步走 Well let's, okay, let's do this two part here.
我打算运行这个 I'm going to run this.
它在这里 Here it is right here.
你可以在Xcode中模拟后台取回 You can actually simulate fetching in the background in X code.
回到Xcode So, I'm going back to X code.
这是我的UI Here's my UI.
从Xcode中 在调试下 我要说 Okay, I'm going to say, from X code, under debug,
模拟后台取回 看我的UI会发生什么 simulate background fetch, and watch what's going to happen to my UI,
我被丢到了后台 oop, I got put in the background.
它在进行取回 Okay. And it's doing a fetch.
它到Flickr进行着那些下载 It's going in Flickr. It's downloading to Flickr and doing all that stuff,
实际上 完成后到这里 and actually if I went like this when it was done,
我可以看到结果 I would see the results in here.
这个你们有些难于看到 Okay, now that's hard for you to see here,
因此 我还打算展示另一个模拟后台取回的方式 so I'm going to show you another way to simulate a background fetch besides this,
我们删除了Photomania 这样表格就空了 so let's delete photo mania so that our tables are empty.
你们可以在这里很清楚地看到 You'll really be able to see it clearly here, oops,
我应该先退出Xcode I should have quit X code first.
这里我来退出 Put that in the step.
我下面要做的是这样 有一种方式 And, so what I'm going to do here is there's a way
来启动你的应用 to launch your application
就像它刚从后台取回中得到启动 as if it just got launched from a background fetch,
做法是编辑scheme and the way to do that is edit scheme.
编辑scheme scheme也就是它如何启动你 If you edit this little scheme, the scheme is how it's launching you.
这里它是在模拟器中启动我们 In this case, it's launching us in a simulator.
编辑scheme 到选项 这里有个小选框 So, if you edit scheme, and you go over to options, there's this little switch here,
由于后台取回的启动 launch due to a background fetch.
换言之 我点击运行时 假装我刚启动 In other words, when I hit run, pretend I just got launched,
因为一个后台取回被发送给我 这就是这个 because a background fetch got sent to me, okay, so that's what that means.
通常 我们不会那样设置 Now, normally, we're not going to set that switch and leave it set like that.
你可以复制这个scheme 这里我来复制一下 You can actually duplicate this scheme, so I have the schema; I'm going to duplicate it.
称它为我的BFF scheme I'm going to call it my BFF scheme.
意思是后台Flickr取回器 That means background Flickr fetcher.
我的BFF scheme中 我将勾选这个 It's my BFF scheme, and in my BFF scheme, I'm going to have that switch set, okay.
在其它scheme 例如回到这个scheme 编辑它 In my other scheme, if I go back to this scheme and edit it,
这个就没有勾选 that switch is not set, okay.
要假装这个正在发生 So, if I want to pretend that's happening,
我到BFF scheme 点运行 I go to my BFF scheme, and I press run.
运行时 可以看到 我是在后台运行 Now, when I run, see it ran me in the background.
另外还有一点我想展示给大家 So, the other two things -- the other thing I really wanted
只是时间有点不够了 to show you here, which I don't really have time to do.
我快点展示吧 也就是 Oh, actually I'll do it real quick is,
我如果是前台app 取回是怎样的 how about fetching when I'm the foreground app.
如果用户正在用我 我想周期性取回 What happens if the user is currently using me, and I want to fetch periodically.
这是完全不同的内容 Okay, that's a totally different thing.
要做到这个 我们需要一个类 叫NSTimer To do that, we're going to use a class called NS timer.
我要写到这里 Okay, so I'm going to put this here.
一旦数据库context可用 Once the database context is available, then I'm going
我就要开启一个Timer 使用NSTimer方法 to fire off a timer using this NS timer method,
scheduleTimerWithTimeInterval schedule timer with time interval.
我要做这个 I want to do this one.
Flickr取回要多频繁呢 How often should we do our Flickr fetch?
Flickr大概每十到十五分钟才会更新一次 Well, it doesn't seem like Flickr updates it more than once every 10 or 15 minutes,
我们这里就每二十分钟取回一次吧 so let's do it every 20 minutes.
这是以秒计的 So, this is in seconds.
我们用二十分钟 目标是我的self So, I'll do 20 minutes, and the target is going to be myself.
换言之 每隔二十分钟 发给我一个方法 In other words, every 20 minutes, send me a method,
而发送给我的方法是 startFlickrFetch 还有参数 and the method to send me is start Flickr fetch, and the argument.
没有用户信息 And, no user info.
抱歉 这里应该是个冒号 Sorry, this has to be a colon.
等下我就会解释为什么 I'll show you why in a second.
然后重复 And repeat, yes,
我希望这个是每二十分钟 发送这个Timer so I want this to just be every 20 minutes at sending this timer.
这个startFlickrFetch为什么需要有一个冒号呢 Okay. So, why is this start Flickr fetch here have to have colon?
这是因为这个Timer在发送它的消息时 That's because this timer when it sends its message,
总有一个参数 如果我说startFlickrFetch it always has an argument, and if I do start Flickr fetch
带有那个参数 发送给你的是NSTimer with the argument, it's the NS timer that sent it to you.
不过这里 我不关心 But, in this case, I don't care.
我只要startFlickrFetch I'm just going to start Flickr fetch.
每二十分钟 我都会发送这个Flickr取回 So, every 20 minutes, I'm going to fire off this Flickr fetch.
这很简单 Okay, so that's easy.
这时你在前台 So, if you're in the foreground,
在后台时 这个Timer不会发送 now this timer will not fire when you're in the background.
它不会发送 不过这没问题 It just will not fire, but that's okay, because we have
因为有后台取回那些在发送 不这么频繁 不过偶尔会 that background fetching thing firing it, not as often, but occasionally.
而现在 我们的app会一直获取新Flickr Okay, but now all the time, our app is getting new Flickr information all the time.
理解吗 Make sense?
好 这就是我要演示的全部 Okay, so that's all I wanted to show you. Hopefully.
这里有很多信息 There's a lot of information there I know.
包括后台取回 We have background fetching.
前台取回 We have foreground fetching.
创建数据库模型 You've got creating the database model.
使用category来创建添加 You got doing the categories to create the adding things.
有TableView 带NSFetchResultsController We got the TableView with NS Fetch Results Controller.
所有代码我都会贴出 I'm going to post all this code.
今晚我会加一些评论 I'll throw some comments in there tonight,
让你们记得所有这些东西 方便你们掌握 so you can remember what all these things do, and you'll be good to go.
作业好运 我们周一见 Good luck with your homework, and I'll see you on Monday.
更多内容 请访问我校官网stanford.edu > For more, please visit us at Stanford.edu.