• Open Menu Close Menu
  • Apple
  • Shopping Bag
  • Apple
  • Mac
  • iPad
  • iPhone
  • Watch
  • TV
  • Music
  • Support
  • Search apple.com
  • Shopping Bag

Lists

Open Menu Close Menu
  • Terms and Conditions
  • Lists hosted on this site
  • Email the Postmaster
  • Tips for posting to public mailing lists
Re: Core Data Merged Models
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Core Data Merged Models


  • Subject: Re: Core Data Merged Models
  • From: Matthew Firlik <email@hidden>
  • Date: Sat, 22 Apr 2006 16:15:19 -0700


On Apr 22, 2006, at 1:11 PM, Matthew Firlik wrote:


On Apr 22, 2006, at 2:13 AM, Jonathon Mah wrote:

The template code for a "Cocoa Core Data Application" creates a managed object model with the following code:

NSMutableSet *allBundles = [[NSMutableSet alloc] init];
[allBundles addObject:[NSBundle mainBundle]];
[allBundles addObjectsFromArray:[NSBundle allFrameworks]];

managedObjectModel = [[NSManagedObjectModel mergedModelFromBundles: [allBundles allObjects]] retain];

What is the purpose of merging models contained in frameworks? In other words, what advantage does this have over [[NSManagedObjectModel alloc] initWithURL:myMomURL] (apart from not needing to know the URL)?

The code is a small convenience to allow the template to work out-of- the-box (meaning, almost immediate turn-around) for those who are developing (just) an application or an application and framework (where the framework contains models as well.) The code to write to load a single model from its URL isn't complicated, though you do need to know the bundle it resides in (exploded here for clarity):


NSBundle *bundle = [NSBundle mainBundle]; /// or some other API ...
NSString *modelPath = [bundle pathForResource: @"MyModel" ofType:@"mom"];
NSURL *modelURL = [NSURL fileURLWithPath: modelPath];
NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL: modelURL]


Obviously if you have more than one model (and you plan to use them in concert), you need to have them merged before adding them to the coordinator. You can do that with the convenience API, or yourself using:

NSArray *models = [NSArray arrayWithObjects: model1, model2, /*(etc) */ nil];
NSManagedObjectModel *mergedModel = [NSManagedObjectModel modelByMergingModels: models];


Which isn't all that difficult, but since you have to locate the bundles for the models anyway to get the URL information, it's just as simple toss the bundles to the mergedModelsFromBundles: API and have it done for you. By the way, the code:

NSManagedObjectModel *model = [NSManagedObjectModel mergedModelFromBundles: nil];

defaults to loading all models from the main bundle ([NSBundle mainBundle]).



If the user added a framework to the system which contained its own MOM, could that render existing stores unreadable by apps using this code?

This is an important thing to think through. It can be dangerous to load models from all linked frameworks/bundles if you are unsure if they have models in them, or what the models actually contain. For example, in cases where separate developers haven't clearly (meaning, uniquely) named their entities, the merging API would complain about having conflicting entity descriptions when it went to merge. (Perhaps a little known fact, but Core Data will ignore an attempt to load the *exact same* entity more than once -- so if you tried to merge a model with itself, Core Data wouldn't be bothered.)


However, the true danger here is when using the SQL stores. When you create a SQL store with a model, Core Data will create tables for ALL entities in the specified model (even if you use a configuration, because the store must be able to support the model even if you change configurations to another collection of entities.) Thus, if you are (unaware that you are) loading models from frameworks, you'll have more tables in your store than you expected. That's not a problem in itself ... *unless* those models happen to change! If they did (including if the model goes away), then the merged model for your application would be different than before, and incompatible with any stores you created with the previous merge.

In summary -- the merged-models API is convenient and safe in situations where you own or are 100% certain of the content and stability of the bundles you are passing to it. For most developers, that probably means (just) passing their bundles or nil to get the correct resulting model. If you want to protect against accidentally loading other models, then the initWithContentsOfURL: method it your best bet. (As an aside, we've already changed the Xcode templates for the next major release to use the URL-based methods: as more frameworks add models, this will make loading more than one model a very conscious decision.)

Finally -- for all framework developers -- if you are worried about this case (where someone might accidentally load your model), or you are concerned about those who might explicitly load your model (because they see a resource with the .mom extension) to "troll" your data content without using your API, you might consider adding a shell-script build phase in Xcode to change the extension of your compiled model file to something other than ".mom." Doing so would mean the merged models API wouldn't "see" the model(s) (since it looks for resources with the ".mom" extension), thus making them accidental-load proof: it does mean any explicit consumers have to use the initWithContentsOfURL: method to load the model (or, go through API you provide), but it might be a good thing.


After seeing Malcolm's post and re-reading the original, I wanted to clarify that there are two separate issues of "getting merged models models from frameworks" here:

Will I get models from frameworks other users add?
As Malcolm pointed out, any/all of the class methods on NSBundle (allFrameworks, allBundles, etc) will return bundles already linked into your application (explicitly or implicitly through others.) Thus, since the template code for the Core Data Application uses these methods, you are ensured to only get models from bundles you are using if you used the mergedModelFromBundles: API. If a developer goes out and adds five brand new frameworks with models, you aren't going to need to worry about them unless you start to link the frameworks. However, if you dynamically load plugins or find yourself using initWithPath: on NSBundle as your application starts up, you should think carefully about how that might affect the resulting model.


What happens when frameworks change over time?
When using merged models from frameworks, the important issues is consistency. If someone changes a model you are currently using, that will make it incompatible with stores used with previous versions. However, as more (framework) developers start to use Core Data, this also means brand NEW models may appear in frameworks you are using today. This is true for third-party frameworks as well as system frameworks (since we use Core Data, too. :) So while accessing a merged model using the "allFrameworks" array from NSBundle might not return any "extra" entities today, it just might in the near future: this is a good reason to use the most explicit/specific/minimal array of bundles necessary to load your models if you use the mergedModelFromBundles API.


- matthew

_______________________________________________
Do not post admin requests to the list. They will be ignored.
Cocoa-dev mailing list      (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden


  • Follow-Ups:
    • Re: Core Data Merged Models
      • From: Jonathon Mah <email@hidden>
References: 
 >Core Data Merged Models (From: Jonathon Mah <email@hidden>)
 >Re: Core Data Merged Models (From: Matthew Firlik <email@hidden>)

  • Prev by Date: Re: Keys of the kingdom
  • Next by Date: Re: Keys of the kingdom
  • Previous by thread: Re: Core Data Merged Models
  • Next by thread: Re: Core Data Merged Models
  • Index(es):
    • Date
    • Thread