我构建了一个大量使用 Core Data 框架的静态库。我可以在我的外部项目中成功使用该库,但前提是我在主项目中包含 .xcdatamodel 文件。这不太理想,因为库的目的是尽可能隐藏实现细节。
在一个单独的问题中,我被告知我不能将资源与库捆绑在一起(现在这对我来说完全有意义)。
那么有没有办法以编程方式让模型被“发现”而不必将模型包含在主项目中?
我构建了一个大量使用 Core Data 框架的静态库。我可以在我的外部项目中成功使用该库,但前提是我在主项目中包含 .xcdatamodel 文件。这不太理想,因为库的目的是尽可能隐藏实现细节。
在一个单独的问题中,我被告知我不能将资源与库捆绑在一起(现在这对我来说完全有意义)。
那么有没有办法以编程方式让模型被“发现”而不必将模型包含在主项目中?
Sascha 的回答让我走上了正轨。.mom
将静态库中的编译文件合并到.mom
宿主项目中的文件相对简单。这是一个简单的例子:
创建一个名为的新 XCode 静态库项目MyStaticLibrary
MyStaticLibrary
在called中创建一个 .xcdatamodel 文件MyStaticLibraryModels.xcdatamodel
,添加一些Entity
s,然后生成头文件和实现。构建MyStaticLibrary
目标时,您将生成一个libMyStaticLibrary.a
二进制文件,但它不会包含编译后的.mom
文件。为此,我们必须创建一个捆绑包。
创建一个类型为 的新构建目标Loadable Bundle
,在 下找到MacOS X > Cocoa
,我们将其称为新目标MyStaticLibraryModels
。
拖入目标MyStaticLibraryModels.xcdatamodel
的Compile Sources
构建阶段MyStaticLibraryModels
。构建MyStaticLibraryModels
目标时,您将生成一个名为的文件MyStaticLibraryModels.bundle
,其中包含已编译的NSManagedObjectModel
文件MyStaticLibraryModels.mom
.
在构建MyStaticLibrary
和MyStaticLibraryModels
Targets之后libMyStaticLibrary.a
,MyStaticLibraryModels.bundle
将MyAwesomeApp
.
MyAwesomeApp
使用CoreData
,有它自己的.xcdatamodel
文件,它会在它自己的构建过程中被编译成一个 .mom 文件。我们想将此.mom
文件与我们导入的文件合并MyStaticLibraryModels.bundle
。在MyAwesomeApp
项目的某个地方,有一个返回MyAwesomeApp
s的方法NSManagedObjectModel
。Apple 为该方法生成的模板如下所示:
...
- (NSManagedObjectModel *)managedObjectModel {
if (managedObjectModel_ != nil) {
return managedObjectModel_;
}
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"MyAwesomeApp" withExtension:@"momd"];
managedObjectModel_ = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return managedObjectModel_;
}
我们将对其进行更改以合并并返回我们NSManagedObjectModel
的 s、MyAwesomApp
s 和BOTH MyStaticLibraryModels
,作为一个单一的组合,NSManagedObjectModel
如下所示:
- (NSManagedObjectModel *)managedObjectModel {
if (managedObjectModel_ != nil) {
return managedObjectModel_;
}
NSMutableArray *allManagedObjectModels = [[NSMutableArray alloc] init];
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"MyAwesomeApp" withExtension:@"momd"];
NSManagedObjectModel *projectManagedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
[allManagedObjectModels addObject:projectManagedObjectModel];
[projectManagedObjectModel release];
NSString *staticLibraryBundlePath = [[NSBundle mainBundle] pathForResource:@"MyStaticLibraryModels" ofType:@"bundle"];
NSURL *staticLibraryMOMURL = [[NSBundle bundleWithPath:staticLibraryBundlePath] URLForResource:@"MyStaticLibraryModels" withExtension:@"mom"];
NSManagedObjectModel *staticLibraryMOM = [[NSManagedObjectModel alloc] initWithContentsOfURL:staticLibraryMOMURL];
[allManagedObjectModels addObject:staticLibraryMOM];
[staticLibraryMOM release];
managedObjectModel_ = [NSManagedObjectModel modelByMergingModels:allManagedObjectModels];
[allManagedObjectModels release];
return managedObjectModel_;
}
这将返回NSManagedObjectModel
与Entity
sMyAwesomeApp
和s 的合并MyStaticLibrary
。
我还创建了自己的使用 Core Data 的静态库。除了静态库之外,我在项目中有另一个捆绑目标,其中我有一个 Copy Bundle Resources 项,它将一些图像和类似的东西复制到捆绑和一个编译源构建阶段,我正在编译 xcdatamodel。
最终的捆绑包将包含所有必要的文件。在依赖于静态库的主项目中,您还必须包含该捆绑包。您的主项目现在可以访问使用核心数据所需的 mom 文件。
要将核心数据与捆绑包中的妈妈一起使用,您必须在代码中创建一个合并的托管对象模型(它可能是主项目也有一些核心数据模型):
- (NSManagedObjectModel *) mergedManagedObjectModel
{
if (!mergedManagedObjectModel)
{
NSMutableSet *allBundles = [[[NSMutableSet alloc] init] autorelease];
[allBundles addObjectsFromArray: [NSBundle allBundles]];
[allBundles addObjectsFromArray: [NSBundle allFrameworks]];
mergedManagedObjectModel = [[NSManagedObjectModel mergedModelFromBundles: [allBundles allObjects]] retain];
}
return mergedManagedObjectModel;
}
通过仅包含捆绑包,您不必提供 xcdatamodel,只需要包含已编译的 mom 文件。
Prairiedogg 的回答有点过时了,这是在 Xcode 5 中执行此操作的教程:http: //bharathnagarajrao.wordpress.com/2014/02/14/working-with-core-data-in-a-static-library/
Sascha Konietzke 的解决方案运行良好,但需要提供一个重要的警告才能使其正常工作。需要先加载包含模型的bundle,否则将不会被包含在数组中并在MOM中合并。
在他的情况下,他可能已经从包中访问了资源,因此包在执行此代码之前已经加载。
我也有一些带有 coredata 的库。我找到了这个模板来管理带有嵌入资源的框架
在新项目上使用它真的很简单(在现有项目上更难应用)但是对于框架构建来说,它真的很酷:-)
请注意,除了使用 xcdatamodel/mom 文件之外,您还可以在代码中创建模型(特别是如果您有一个简单的模型),这样您就不需要为资源创建额外的包。这是一个简单的示例,其中一个表包含两个属性:
- (NSManagedObjectModel *)coreDataModel
{
NSManagedObjectModel *model = [NSManagedObjectModel new];
NSEntityDescription *eventEntity = [NSEntityDescription new];
eventEntity.name = @"EventEntity";
eventEntity.managedObjectClassName = @"EventEntity";
NSAttributeDescription *dateAttribute = [NSAttributeDescription new];
dateAttribute.name = @"date";
dateAttribute.attributeType = NSDateAttributeType;
dateAttribute.optional = NO;
NSAttributeDescription *typeAttribute = [NSAttributeDescription new];
typeAttribute.name = @"type";
typeAttribute.attributeType = NSStringAttributeType;
typeAttribute.optional = NO;
eventEntity.properties = @[dateAttribute, typeAttribute];
model.entities = @[eventEntity];
return model;
}
这是一个关于从代码创建模型的教程:https ://www.cocoanetics.com/2012/04/creating-a-coredata-model-in-code/
同样基于这种方法,我创建了一个小型且易于使用的库,它可能满足您的需求,称为LSMiniDB,因此您也可以检查它。
同样在我的情况下,在使用 NSManagedObject 子类的属性时,我在控制台上收到了诸如“警告:动态访问器无法找到@property 实现... ”之类的警告。我能够通过将这些属性移动到类接口/实现而不是将它们放在单独文件中的类别中来解决这个问题(当前,xcode 默认情况下生成此代码,该代码拆分为单独的文件 ClassName+CoreDataClass 和 ClassName+CoreDataProperties 与一个类以及每个子类的类别)。
不,在 iPhone 应用程序中使用非 Apple 框架的限制确实改变了相对于 OS X 的依赖游戏。大多数 iPhone“框架”(例如 Google 的 Mac 工具箱、Core Plot 等)实际上建议您将源代码包含在您的主要应用程序项目,而不是链接产品(即静态库)。我认为社区的共识是,在 iPhone 上,可以期望框架的消费者必须做一些“手动”工作才能使用你的库。在您的情况下,这包括主项目中的 xcdatamodel 文件。与大多数 Objective-C 一样,告诉您的用户不要使用实现细节并保留它。
Sascha 回答的 Swift 2 版本:
lazy var managedObjectModel: NSManagedObjectModel = {
// The managed object model for the application. This property is not optional. It is a fatal error for the application not to be able to find and load its model.
var allBundles = NSMutableSet()
allBundles.addObjectsFromArray(NSBundle.allBundles())
allBundles.addObjectsFromArray(NSBundle.allFrameworks())
let model = NSManagedObjectModel.mergedModelFromBundles(allBundles.allObjects as? [NSBundle])
return model!
}()