Re: Core Data Merged Models
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