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

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(Core Data)