Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What are NSManagedObjectContext best practices?

I'm working with a Navigation Controller based iOS app. There are multiple tableView screens that pull and save data from a Core Data persistent store. Most of the data for the different table views comes from NSFetchedResultsController instances or NSFetchRequests.

The app works as intended but I have been getting a few random crashes and glitches that seem to be related to Core Data. For example sometimes when I save the context the app will crash but not always. Another thing I've been seeing is the very first tableView doesn't always update the reflect the data that was modified in it's detail view.

Currently I'm passing around a single Managed Object Context that was created in the app delegate to each of the different view controllers by setting the context property of the view controller just before I push it onto the navigation stack.

This seems like a clunky, hacky way of getting the job done. Is there a better design pattern to use?

I noticed in one of the WWDC sessions using delegation but I've never used creating my own delegates before and haven't been able to puzzle it out of the WWDC session.

Thanks.

=)

like image 785
Aaronium112 Avatar asked Feb 20 '11 15:02

Aaronium112


People also ask

What is the purpose of NSManagedObjectContext?

A managed object context is an instance of NSManagedObjectContext . Its primary responsibility is to manage a collection of managed objects. These managed objects represent an internally consistent view of one or more persistent stores.

When should I use Core Data?

Use Core Data to save your application's permanent data for offline use, to cache temporary data, and to add undo functionality to your app on a single device. To sync data across multiple devices in a single iCloud account, Core Data automatically mirrors your schema to a CloudKit container.

Is NSManagedObjectContext thread safe?

NSManagedObjectContext is not thread safe.


3 Answers

Use singleton NSManagedObjectContext for all Controllers isn't a best practice.

Each Controller should have your own Context to manage specific, sometimes atomic, operations at document store.

Think if you can edit a NSManagedObject attached to Controller that pass the same Context to other Controller that will select another instance to delete or edit.. you can lost the controll about modified states.

When you create a view controller, you pass it a context. You pass an existing context, or (in a situation where you want the new controller to manage a discrete set of edits) a new context that you create for it. It’s typically the responsibility of the application delegate to create a context to pass to the first view controller that’s displayed.

http://developer.apple.com/library/ios/#documentation/DataManagement/Conceptual/CoreDataSnippets/Articles/stack.html

like image 197
seufagner Avatar answered Oct 15 '22 08:10

seufagner


1) Use a singleton for your CoreData setup (NSPesistentStoreCoordinator, NSManagedObjectModel & NSManagedObjectContext). You can use this singleton to execute the fetch requests you created in your Models and to add or delete Entities to your Context.

2) Delegates are not that hard. Following is a sample:

@class SomeClass

@protocol SomeClassDelegate <NSObject> //Implements the NSObject protocol
- (void) someClassInstance:(SomeClass *)obj givesAStringObject:(NSString *)theString;
- (BOOL) someClassInstanceWantsToKnowABooleanValue:(SomeClass *)obj //Call to delegate to get a boolean value

@optional
- (NSString *) thisMethodIsOptional; 

@end

@interface SomeClass : NSObject {
   id<SomeClassDelegate> delegate;
   //Other instance variables omitted.
}
@property (assign) id<SomeClassDelegate> delegate;
@end

@implementation SomeClass
@synthesize delegate;
- (void) someMethodThatShouldNotifyTheDelegate {
   NSString *hello = @"Hello";
   if (self.delegate != nil && [self.delegate respondsToSelector:@selector(someClassInstance:givesAStringObject:)]) {
      [self.delegate someClassInstance:self givesAStringObject:hello];
   }
}
@end

Option 1 could be something like this, you will have to setup the variables in the init of the object (and implement the singleton ofcourse):

#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>

@interface CoreDataUtility : NSObject {
@private
    NSManagedObjectModel *managedObjectModel;
    NSManagedObjectContext *managedObjectContext;
    NSPersistentStoreCoordinator *persistentStoreCoordinator;
}

+ (CoreDataUtility *)sharedCoreDataUtility;
- (NSEntityDescription *) entityDesctiptionForName:(NSString *)name;
- (NSMutableArray *) executeRequest:(NSFetchRequest *)request;
- (id) getInsertedObjectForEntity:(NSString *)entity;
- (void) deleteAllObjects:(NSString *) entityName;
- (void) deleteManagedObject:(NSManagedObject *)object;
- (void) saveContext;

@end
like image 41
Mats Stijlaart Avatar answered Oct 15 '22 07:10

Mats Stijlaart


Currently I'm passing around a single Managed Object Context that was created in the app delegate to each of the different view controllers...This seems like a clunky, hacky way of getting the job done. Is there a better design pattern to use?

There's nothing particularly special about a managed object context in this respect, it's just another object that your view controller may need to do its job. Whenever you're setting up an object to perform a task, there are at least three strategies that you can use:

  1. Give the object everything it needs to get the job done.

  2. Give the object a helper that it can use to make decisions or get additional information.

  3. Build enough knowledge about other parts of the application into the object that it can go get the information it needs.

What you're doing right now sounds like the first strategy, and I'd argue that it's often the best because it makes your view controllers more flexible, less dependant on other parts of the app. By providing the MOC to your view controllers, you leave open the possibility that you might someday use that same view controller with a different context.

Jayallengator makes the helpful observation that every managed object has a reference to its context, and if you're passing around specific managed objects you don't also need to pass along the context. I'd take that a step further: if you're passing specific managed objects to your view controller, the view controller often won't need to know about the context at all. For example, you might keep Game objects in your data store, but a GameBoardViewController will probably only care about the one Game that's being played, and can use that object's interface to get any related objects (Player, Level, etc.). Perhaps these observations can help you streamline your code.

The second strategy is delegation. You'll usually use a protocol when you use delegation, so that your object knows what messages it can send its helper without knowing anything else about the helper. Delegation is a way to introduce a necessary dependency into your code in a limited, well-defined way. For example, UITableView knows that it can send any of the messages defined in the UITableViewDelegate protocol to its delegate, but it doesn't need to know anything else about the delegate. The delegate could be a view controller, or it could be some other kind of object; the table doesn't care. The table's delegate and data source are often the same object, but they don't have to be; again, the table doesn't care.

The third strategy is to use global variables or shared objects (which is what people usually mean when they talk about singletons). Having a shared object that you can access from anywhere in your code is certainly easy, and you don't have that "klunky" extra line of code that configures your object, but it generally means that you're locking your view controllers in to using that shared object and no other. It's a lot like gluing a hammer to your hand because you know for certain that that hammer is the tool you need. Works great for pounding nails, but it can be painful if you later discover that you'd like to use the same hand for driving screws or eating dinner.

like image 27
Caleb Avatar answered Oct 15 '22 06:10

Caleb