OC开发_Storyboard——Core Data

一 、NSManagedObjectContext

1、我们要想操作Core Data,首先需要一个NSManagedObjectContext
2、那我们如何获得Context呢:创建一个UIManagedDocument

二、UIManagedDocument

1、UIManagedDocument是一系列用于管理存储的机制:
  【将Core Data数据库放入某存储空间,相当于是管理core data 数据库的存储,所以我们只需要打开和存储】
2、那我们如何得到UIManagedDocument呢?如何在用户文档中创建一个UIDocument?

1 //(1文件管理器能够给我们一个用户文件目录的URL
2 NSFileManager *fileManager = [NSFileManager defaultManager];
3 NSURL *documentsDirectory = [[fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] firstObject];
4 
5 //(2然后加上我们想要的文档名
6 NSString *documentName = @“MyDocument”;
7 NSURL *url = [documentsDirectory URLByAppendingPathComponent:documentName];
8 //(3这个URL就是core data数据库存储的地方
9 UIManagedDocument *document = [[UIManagedDocument alloc] initWithFileURL:url];

3、但是我们创建的这个文档还并不存在于我们的磁盘中,还需要存储到磁盘

1 //(1先判断是否已经存在于磁盘
2 BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:[url path]];!
3 //(2.1如果已经存在于磁盘,则直接打开
4 [document openWithCompletionHandler:^(BOOL success) { /* block to execute when open */ }]; !
5 //(2.2否则还要先存储到磁盘
6 [document saveToURL:url forSaveOperation:UIDocumentSaveForCreating competionHandler:^(BOOL success) { /* block to execute when create is done */ }];

     e.g.下面我们看一个综合了上面的具体的例子,如何通过UIManagedDocument得到NSManagedObjectContext:

 1 self.document = [[UIManagedDocument alloc] initWithFileURL:(URL *)url];
 2 if ([[NSFileManager defaultManager] fileExistsAtPath:[url path]]) {
 3     [document openWithCompletionHandler:^(BOOL success) {
 4         if (success) [self documentIsReady];
 5         if (!success) NSLog(@“couldn’t open document at %@”, url);
 6     }];
 7 } else {
 8     [document saveToURL:url forSaveOperation:UIDocumentSaveForCreating
 9       completionHandler:^(BOOL success) {
10           if (success) [self documentIsReady];
11           if (!success) NSLog(@“couldn’t create document at %@”, url);
12       }];
13 }

     然后我们在 documentIsReady做一些操作从而获得context:

 1 - (void)documentIsReady
 2 {
 3     /*对应的状态有:
 4    UIDocumentStateClosed (还没有打开或者创建)
 5    UIDocumentStateSavingError (completion handler保存没有成功)
 6    UIDocumentStateEditingDisabled (重试)
 7    UIDocumentStateInConflict(例如有其他人在使用更新,有冲突到等)*/
 8 
 9    if (self.document.documentState == UIDocumentStateNormal) {
10        //如果成功的话,我们就获得了我们需要的context了,然后操作core data
11        NSManagedObjectContext *context = self.document.managedObjectContext;
12    }
13 }

4、注意的点
【但要注意的点1:上面UIManagedDocument的打开、关闭或者创建(存储)都是异步执行的】
【需要注意的点2: UIManagedDocument是自动保存的,我们也可以调用上面的自己保存,对应的关闭也是自动关闭的】
【需要注意的点3: UIManagedDocument是多实例的,也就是说可以多控制器同时操作,但对应的同时只有一个可写】

5、广播站:
比如我们在一个文档中修改了数据CoreData,但是同时 另一个并没有能马上看到,这是因为它们所用的Context不同,要想能看到需要使用NSNotiFication广播站


三、NSNotiFication广播站

1、如何注册一个广播

 1 - (void)viewDidAppear:(BOOL)animated
 2 {
 3    [super viewDidAppear:animated];
 4    [center addObserver:self
 5               selector:@selector(contextChanged:)
 6                   name:NSManagedObjectContextDidSaveNotification
 7                 object:document.managedObjectContext]; // don’t pass nil here!
 8 }
 9 - (void)viewWillDisappear:(BOOL)animated
10 {
11     [center removeObserver:self
12                       name:NSManagedObjectContextDidSaveNotification
13                     object:document.managedObjectContext];
14     [super viewWillDisappear:animated];
15 }

2、广播得到消息之后能做什么?如何在contextChanged里操作?

(1 可以取回我的所有对象

1 - (void)contextChanged:(NSNotification *)notification
2 {
3     //  notification.userInfo 返回给我们的是一个字典包含以下的key
4     NSInsertedObjectsKey //插入的对象数组
5     NSUpdatedObjectsKey // 有属性更改的对象数组
6     NSDeletedObjectsKey // 有删除的对象数组
7 }

(2 Merging changes:只要把notification传给它,它会自动帮我们把所有的变化合并到我们的context中
   - (void)mergeChangesFromContextDidSaveNotification:(NSNotification *)notification;

 

四、Core Data 

上面的操作都完备,我们就可以对我们的数据库进行增删改的操作了
【这些操作是在内存中,不是在磁盘,但是别忘记了Document是自动保存的,所以最终还是会保存到磁盘的,只要文档保存了,Context就保存了

1、插入

1    NSManagedObjectContext *context = aDocument.managedObjectContext;
2    //实体的名称:@“EntityBook”,返回NSManagedObject对象【数据库所有对象都是它或者它的子类】
3    NSManagedObject *book = [NSEntityDescription insertNewObjectForEntityForName:@“EntityBook” inManagedObjectContext:context];
4    //设置属性
5    - (id)valueForKey:(NSString *)key; 
6    - (void)setValue:(id)value forKey:(NSString *)key; 
7    或者也可以用:valueForKeyPath:/setValue:forKeyPath

设置属性例如:
NSString *myThumbnail = book.thumbnailURL;
book.lastViewedDate = [NSDate date];
book.whoTook = ...; //这里的whoTook指的是另一张表的关联关系
book.whoTook.name = @“CS193p Instructor”;

 

2、删除

//(1要注意的是这也不是马上删除,而是需要在自动保存之后才会删除,但是引用到book的地方需要在这个操作之后设置为nil
[aDocument.managedObjectContext deleteObject:book];

//(2 这个方法通常放在类别中,用于删除操作之后的某些更新
- (void)prepareForDeletion{}

 

3、查询

(1 NSFetchRequest 提出请求从数据库请求对象
指定要取回的实体、指定取回的对象大小数量、NSSortDescriptors排序、NSPredicate谓词哪一些数据

NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@“EntityBook”];
request.fetchBatchSize = 20;
request.fetchLimit = 100;
request.sortDescriptors = @[sortDescriptor];
request.predicate = ...;

     (2 NSSortDescriptor 排序

1 NSSortDescriptor *sortDescriptor =
2         [NSSortDescriptor sortDescriptorWithKey:@“title”  //排序的键
3                                       ascending:YES       //YES是按字母排序,NO是反字母排序
4                                        selector:@selector(localizedStandardCompare:)]; //在排序中的对比,这里的localizedStandardCompare 指代像Mac finder中的排序方式一般
5    

(3 NSPredicate谓词

 1     NSString *serverName = @“IOS-7”;
 2     NSPredicate *predicate =
 3     [NSPredicate predicateWithFormat:@“bookName contains %@”, serverName];
 4     
 5     @“uniqueId = %@”, [flickrInfo objectForKey:@“id”]  
 6     @“name contains[c] %@”, (NSString *) 
 7     @“viewed > %@”, (NSDate *) 
 8     @“whoTook.name = %@”, (NSString *)  
 9     @“any photos.title contains %@”, (NSString *)  
10     @“(name = %@) OR (title = %@)”
11     @“photos.@count > 512     @“photos.photo.title.length" 
13     [propertyListResults valueForKeyPath:@“photos.photo.@avg.latitude”]
14     
15        // 复合谓词
16     NSArray *array = @[predicate1, predicate2];
17     NSPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:array];

更多的使用可以查询:
https://developer.apple.com/library/ios/documentation/cocoa/conceptual/KeyValueCoding/Articles/CollectionOperators.html.

       demo请求的例子:

1     NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@“Photographer”];
2     NSDate *yesterday = [NSDate dateWithTimeIntervalSinceNow:-24*60*60]; 
3     request.predicate = [NSPredicate predicateWithFormat:@“any photos.uploadDate > %@”, yesterday]; 
4     request.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@“name” ascending:YES]];
5     
6     NSManagedObjectContext *context = aDocument.managedObjectContext; 
7     NSError *error;
8     NSArray *photographers = [context executeFetchRequest:request error:&error]; 

 

五. 生成实体的类

1、方法
Editor->Create NSManagedObject Subclass => 选择Model => 选择要生成的数据表EntityBook

2、可以修改这些系统生成的类?=> 最好不要,我们可以采用类别的方法,也就是Categories
【Categories类别可以添加方法到一个类,而不用创建它额的子类,你甚至不需要有该类的源代码】

 

六、Categories类别

不能使用实例变量或者任何存储数据
1、声明

1 @interface EntityBook (AddOn)
2 - (NSString *)note;
3 @property (readonly) BOOL isOld;
4 @end

 

2、实现

 1 @implementation EntityBook (AddOn)
 2 - (NSString *)note //要注意:note不是数据库表的属性,但是这里的bookName uploadDate是属性
 3 {
 4    NSString *bookNote = [NSString stringWithFormat:@"%@:lalalallal", self.bookName];
 5    return bookNote;
 6 }
 7 - (BOOL)isOld //  
 8 {
 9    return [self.uploadDate timeIntervalSinceNow] > -24*60*60;
10 }
11 @end

3、大多数情况下的实现

 1 @implementation EntityBook (Create)
 2 
 3 + (EntityBook *)bookWithData:(NSDictionary *)Data inManagedObjectContext:(NSManagedObjectContext *)context
 4 {
 5    EntityBook *book = ...; // 查看具体某一本书是否存在
 6    if (!book) 
 7    {
 8       book = [NSEntityDescription insertNewObjectForEntityForName:@“EntityBook” inManagedObjectContext:context];
 9    }
10    return book;
11 }
12 @end

 

七、线程安全

1、NSManagedObjectContext并不线程安全,任何使用在下面的BLock里面去执行 它会对Context在安全队列中执行

[context performBlock:^{ or performBlockAndWait:
       //使用对Context做的事情,例如插入对象、查询等等
}];

 

八、NSFetchedResultsController 

   [使得Core Data和UITableViewController能够相辅相成。]

  1、例如:

- (NSUInteger)numberOfSectionsInTableView:(UITableView *)sender

{

     return [[self.fetchedResultsController sections] count];

}

- (NSUInteger)tableView:(UITableView *)sender numberOfRowsInSection:(NSUInteger)section

{

     return [[[self.fetchedResultsController sections] objectAtIndex:section] numberOfObjects];

}

- (UITableViewCell *)tableView:(UITableView *)sender cellForRowAtIndexPath:(NSIndexPath *)indexPath

{

    UITableViewCell *cell = ...;

    // 或者 Book *book = (Book *) ...

    NSManagedObject *managedObject =  [self.fetchedResultsController objectAtIndexPath:indexPath];

    return cell;

}

  2、那么如何构建一个NSFetchedResultsController呢?

NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@“EntityBook”];

request.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@“title” ...]];

//关联另一张表的查询,比如说作者表中的作者名称,作为我们book表的WhoTook

request.predicate = [NSPredicate predicateWithFormat:@“whoTook.name = %@”, authorName];

NSFetchedResultsController *frc = [[NSFetchedResultsController alloc]  

              initWithFetchRequest:(NSFetchRequest *)request

                    managedObjectContext:(NSManagedObjectContext *)context

                    sectionNameKeyPath:(NSString *)keyThatSaysWhichSectionEachManagedObjectIsIn 

                    cacheName:@“MyPhotoCache”// 指定为nil的话,就不会执行缓存处理[永久性在磁盘中]:所以只针对总有相同FetChRequest的TableView

      ]; 

  3、让NSFetchwsResultController和TableView关联起来有两种方式,一种是利用它实现所有UITableViewDataSource的东西

   第二种就是设置委托:

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject

       atIndexPath:(NSIndexPath *)indexPath

     forChangeType:(NSFetchedResultsChangeType)type

      newIndexPath:(NSIndexPath *)newIndexPath

{

    // 更新TableView[可以利用CoreDataTableViewController]

}

  4、CoreDataTableViewController

 

 

 

 

 

 

 

 

 

posted @ 2015-05-03 11:03  daomul  阅读(686)  评论(0编辑  收藏  举报