iPhone 静态库中的核心数据

发布于 2024-08-10 23:03:53 字数 318 浏览 7 评论 0原文

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

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

那么有没有一种方法可以以编程方式允许“发现”模型,而不必将模型包含在主项目中?

I've built a static library that makes heavy use of the Core Data framework. I can successfully use the library in my external project, but ONLY if I include the .xcdatamodel file in the main project. That is less than ideal, since the point of the library was to hide implementation details to the maximum possible.

In a separate question, I was informed that I cannot bundle resources with a library (which makes complete sense to me now).

So is there a way to programatically allow the model to be 'discovered' without having to include the model in the main project?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

巡山小妖精 2024-08-17 23:03:53

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

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

    的项目

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

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

  4. MyStaticLibraryModels.xcdatamodel 拖到 MyStaticLibraryModels 目标的 Compile Sources 构建阶段。当您构建 MyStaticLibraryModels 目标时,您将生成一个名为 MyStaticLibraryModels.bundle 的文件,它将包含已编译的 NSManagedObjectModel 文件,MyStaticLibraryModels .mom

  5. 构建 MyStaticLibraryMyStaticLibraryModels 目标后,拖动 libMyStaticLibrary.a(以及任何关联的模型头文件)和 MyStaticLibraryModels.bundle 到您的宿主项目 MyAwesomeApp 中。

  6. MyAwesomeApp 使用 CoreData,有自己的 .xcdatamodel 文件,该文件将在其自己的构建过程中编译成 .mom 文件。我们希望将此 .mom 文件与我们在 MyStaticLibraryModels.bundle 中导入的文件合并。在 MyAwesomeApp 项目中的某个位置,有一个方法返回 MyAwesomeAppNSManagedObjectModel。 Apple 为此方法生成的模板如下所示:

...

- (NSManagedObjectModel *)managedObjectModel {
  if (managedObjectModel_ != nil) {
    return managedObjectModel_;
  }
  NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"MyAwesomeApp" withExtension:@"momd"];
  managedObjectModel_ = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];    
  return managedObjectModel_;
}

我们将更改此方法以合并并返回 NSManagedObjectModelMyAwesomAppMyStaticLibraryModels< /code>,作为单个组合的 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 以及来自两个 MyAwesomeApp 的 EntityMyStaticLibrary

Sascha's answer got me on the right track. Merging a compiled .mom file from a static library into the .mom file from a host project was relatively simple. Here's a trivial example:

  1. Create a new XCode Static Library
    project called MyStaticLibrary

  2. Create an .xcdatamodel file in MyStaticLibrary called MyStaticLibraryModels.xcdatamodel, add some Entitys, then generate the headers and implementations. When you build the MyStaticLibrary target, you'll generate a libMyStaticLibrary.a binary file, but it won't include the compiled .mom file. For that we have to create a bundle.

  3. Create a new build target of type Loadable Bundle, found under MacOS X > Cocoa, let's call the new Target MyStaticLibraryModels.

  4. Drag MyStaticLibraryModels.xcdatamodel into the Compile Sources build phase of the MyStaticLibraryModels Target. When you build the MyStaticLibraryModels Target, you will generate a file called MyStaticLibraryModels.bundle and it will contain the compiled NSManagedObjectModel file, MyStaticLibraryModels.mom.

  5. After building both the MyStaticLibrary and MyStaticLibraryModels Targets, drag libMyStaticLibrary.a (along with any associated Model header files) and MyStaticLibraryModels.bundle into your host project, MyAwesomeApp.

  6. MyAwesomeApp uses CoreData, has it's own .xcdatamodel file which will get compiled into a .mom file during its own build process. We want to merge this .mom file with the one we imported in MyStaticLibraryModels.bundle. Somewhere in the MyAwesomeApp project, there is a method that returns MyAwesomeApps NSManagedObjectModel. The Apple generated template for this method looks like this:

...

- (NSManagedObjectModel *)managedObjectModel {
  if (managedObjectModel_ != nil) {
    return managedObjectModel_;
  }
  NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"MyAwesomeApp" withExtension:@"momd"];
  managedObjectModel_ = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];    
  return managedObjectModel_;
}

We will alter this to merge and return BOTH of our NSManagedObjectModels, MyAwesomApps and MyStaticLibraryModels, as a single, combined NSManagedObjectModel like so:

- (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_;
}

This will return the merged NSManagedObjectModel with the Entitys from both MyAwesomeApp and MyStaticLibrary.

無心 2024-08-17 23:03:53

我还创建了自己的使用 Core Data 的静态库。除了静态库之外,我在项目中还有另一个捆绑目标,其中有一个“复制捆绑资源”项,它将一些图像和类似内容复制到捆绑中,以及一个“编译源”构建阶段,其中我正在编译 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文件。

I also created my own static library that uses Core Data. Besides the static library I have a another bundle target in the project where I have a Copy Bundle Resources item, that copies some images and things like that into the bundle and a Compile Sources build phase, where I am compiling the xcdatamodel.

The final bundle will contain all the necessary files. In your main project that relies on the static library you have to include that bundle as well. Your main project will now have access to the mom file that is needed to use core data.

To use core data with the mom from the bundle you have to create a merged managed object model in your code (it might be the main project has some core data model as well):


- (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;
}


By just including the bundle you will not have to give out the xcdatamodel, only the compiled mom file needs to be included.

丶视觉 2024-08-17 23:03:53

我也有一些带有 coredata 的库。
我发现这个模板用于管理具有嵌入资源的框架,

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

https://github.com/kstenerud/iOS-通用框架

i have some library with coredata too.
i have found this template for manage a framework with embed ressources

it's really simple to use on a new project ( more difficult to apply on existing )
but for framewoks build, it's really cool :-)

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

苏佲洛 2024-08-17 23:03:53

Sascha Konietzke 的解决方案效果很好,但需要提供一个重要的警告才能使其发挥作用。包含模型的bundle需要先加载,否则不会包含在数组中并合并到MOM中。

在他的例子中,他可能已经访问了捆绑包中的资源,因此在执行此代码之前捆绑包已经加载。

Sascha Konietzke's solution works well, but there is one important caveat that needs to be provided for it to work. The bundle containing the model needs to be loaded first, otherwise it will not be included in the array and merged in the MOM.

In his case he has probably already accessed resources from the bundle therefore the bundle was already loaded prior to this code being executed.

故笙诉离歌 2024-08-17 23:03:53

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

Prairiedogg's answer is a little outdated, here's a tutorial on doing this in Xcode 5: http://bharathnagarajrao.wordpress.com/2014/02/14/working-with-core-data-in-a-static-library/

命比纸薄 2024-08-17 23:03:53

请注意,您还可以在代码中创建模型(特别是如果您有一个简单的模型),而不是使用 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 )以及每个子类的类别)。

Note that instead of using xcdatamodel/mom file you can also create your model in code (especially if you have a simple model) and this way you won't need to create an additional bundle for resources. Here is a simple example with one table that contains two attributes:

- (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;
}

Here is a tutorial about creating model from code: https://www.cocoanetics.com/2012/04/creating-a-coredata-model-in-code/

Also based on this approach I created a small and easy to use library that might fit your needs called LSMiniDB so you can check it also.

Also in my case I had warnings such as "warning: dynamic accessors failed to find @property implementation..." on the console while using properties of NSManagedObject subclasses. I was able to fix that by moving those properties to a class interface/implementation instead of having them in a category in a separate file (currently xcode by default is generating this code splited into separate files ClassName+CoreDataClass and ClassName+CoreDataProperties with a class and a category for each subclass).

浮生未歇 2024-08-17 23:03:53

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

No, the limitation on using non-Apple frameworks in an iPhone app really changes the dependency game relative to OS X. Most iPhone "frameworks" (e.g. Google's toolbox for Mac, Core Plot, etc.) actually recommend that you include the source in your main application project rather than linking a product (i.e. a static library). I think the community consensus is that, on iPhone, it's OK to expect consumers of your framework to have to do a little "manual" work to use your library. In your case, this is including the xcdatamodel file in the main project. As with most of Objective-C, tell your users not to make use of the implementation details and leave it at that.

杀手六號 2024-08-17 23:03:53

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!
}()

Swift 2 version for Sascha's answer:

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!
}()
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文