43

我构建了一个大量使用 Core Data 框架的静态库。我可以在我的外部项目中成功使用该库,但前提是我在主项目中包含 .xcdatamodel 文件。这不太理想,因为库的目的是尽可能隐藏实现细节。

在一个单独的问题中,我被告知我不能将资源与库捆绑在一起(现在这对我来说完全有意义)。

那么有没有办法以编程方式让模型被“发现”而不必将模型包含在主项目中?

4

8 回答 8

59

Sascha 的回答让我走上了正轨。.mom将静态库中的编译文件合并到.mom宿主项目中的文件相对简单。这是一个简单的例子:

  1. 创建一个名为的新 XCode 静态库项目MyStaticLibrary

  2. MyStaticLibrary在called中创建一个 .xcdatamodel 文件MyStaticLibraryModels.xcdatamodel,添加一些Entitys,然后生成头文件和实现。构建MyStaticLibrary目标时,您将生成一个libMyStaticLibrary.a二进制文件,但它不会包含编译后的.mom文件。为此,我们必须创建一个捆绑包。

  3. 创建一个类型为 的新构建目标Loadable Bundle,在 下找到MacOS X > Cocoa,我们将其称为新目标MyStaticLibraryModels

  4. 拖入目标MyStaticLibraryModels.xcdatamodelCompile Sources构建阶段MyStaticLibraryModels。构建MyStaticLibraryModels目标时,您将生成一个名为的文件MyStaticLibraryModels.bundle,其中包含已编译的NSManagedObjectModel文件MyStaticLibraryModels.mom.

  5. 在构建MyStaticLibraryMyStaticLibraryModelsTargets之后libMyStaticLibrary.aMyStaticLibraryModels.bundleMyAwesomeApp.

  6. MyAwesomeApp使用CoreData,有它自己的.xcdatamodel文件,它会在它自己的构建过程中被编译成一个 .mom 文件。我们想将此.mom文件与我们导入的文件合并MyStaticLibraryModels.bundle。在MyAwesomeApp项目的某个地方,有一个返回MyAwesomeApps的方法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、MyAwesomApps 和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_;
}

这将返回NSManagedObjectModelEntitysMyAwesomeApp和s 的合并MyStaticLibrary

于 2011-01-06T00:13:25.537 回答
31

我还创建了自己的使用 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 文件。

于 2009-11-14T13:37:13.990 回答
2

Prairiedogg 的回答有点过时了,这是在 Xcode 5 中执行此操作的教程:http: //bharathnagarajrao.wordpress.com/2014/02/14/working-with-core-data-in-a-static-library/

于 2014-07-31T16:59:10.870 回答
2

Sascha Konietzke 的解决方案运行良好,但需要提供一个重要的警告才能使其正常工作。需要先加载包含模型的bundle,否则将不会被包含在数组中并在MOM中合并。

在他的情况下,他可能已经从包中访问了资源,因此包在执行此代码之前已经加载。

于 2012-02-28T01:33:51.063 回答
2

我也有一些带有 coredata 的库。我找到了这个模板来管理带有嵌入资源的框架

在新项目上使用它真的很简单(在现有项目上更难应用)但是对于框架构建来说,它真的很酷:-)

https://github.com/kstenerud/iOS-Universal-Framework

于 2011-12-02T17:45:04.783 回答
2

请注意,除了使用 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 与一个类以及每个子类的类别)。

于 2017-10-09T16:18:58.953 回答
1

不,在 iPhone 应用程序中使用非 Apple 框架的限制确实改变了相对于 OS X 的依赖游戏。大多数 iPhone“框架”(例如 Google 的 Mac 工具箱、Core Plot 等)实际上建议您将源代码包含在您的主要应用程序项目,而不是链接产品(即静态库)。我认为社区的共识是,在 iPhone 上,可以期望框架的消费者必须做一些“手动”工作才能使用你的库。在您的情况下,这包括主项目中的 xcdatamodel 文件。与大多数 Objective-C 一样,告诉您的用户不要使用实现细节并保留它。

于 2009-11-10T22:31:56.093 回答
0

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!
}()
于 2015-11-26T15:34:07.570 回答