什么是 NSManagedObjectContext 最佳实践?

Posted

技术标签:

【中文标题】什么是 NSManagedObjectContext 最佳实践?【英文标题】:What are NSManagedObjectContext best practices? 【发布时间】:2011-02-20 15:16:01 【问题描述】:

我正在使用基于导航控制器的 ios 应用程序。有多个 tableView 屏幕可以从 Core Data 持久存储中提取和保存数据。不同表视图的大部分数据来自 NSFetchedResultsController 实例或 NSFetchRequests。

该应用程序按预期运行,但我遇到了一些似乎与 Core Data 相关的随机崩溃和故障。例如,有时当我保存上下文时,应用程序会崩溃,但并非总是如此。我看到的另一件事是第一个 tableView 并不总是更新反映在其详细视图中修改的数据。

目前,我通过在将视图控制器推送到导航堆栈之前设置视图控制器的上下文属性,将在应用程序委托中创建的单个托管对象上下文传递给每个不同的视图控制器。

这似乎是完成工作的一种笨拙、笨拙的方式。有没有更好的设计模式可以使用?

我在一次 WWDC 会议中注意到使用委托,但我之前从未使用过创建自己的委托,并且无法将其从 WWDC 会议中解开。

谢谢。

=)

【问题讨论】:

【参考方案1】:

对所有控制器使用单例 NSManagedObjectContext 并不是最佳实践。

每个控制器都应该有自己的上下文来管理文档存储中的特定操作,有时是原子操作。

考虑一下您是否可以编辑附加到 Controller 的 NSManagedObject,将相同的 Context 传递给其他 Controller,该 Controller 将选择另一个要删除或编辑的实例。您可能会失去对修改状态的控制。

当你创建一个视图控制器时,你传递给它一个上下文。你通过一个 现有上下文,或(在您想要新控制器的情况下 管理一组离散的编辑)您为其创建的新上下文 它。通常由应用程序委托负责 创建一个上下文以传递给第一个视图控制器 显示出来。

http://developer.apple.com/library/ios/#documentation/DataManagement/Conceptual/CoreDataSnippets/Articles/stack.html

【讨论】:

【参考方案2】:

1) 为您的 CoreData 设置使用单例(NSPesistentStoreCoordinator、NSManagedObjectModel 和 NSManagedObjectContext)。您可以使用此单例来执行您在模型中创建的获取请求,并将实体添加或删除到您的上下文中。

2) 代表并不难。以下是一个示例:

@class SomeClass

@protocol SomeClassDelegate <NSObject> //Implements the NSObject protocol
- (void) someClassInstance:(SomeClass *)obj givesAStringObject:(NSString *)theString;
- (BOOL) someClassInstanceWantsToKnowABooleanValue:(SomeClass *)obj //Call to delegate to get a boolean value

@optional
- (NSString *) thisMethodIsOptional; 

@end

@interface SomeClass : NSObject 
   id<SomeClassDelegate> delegate;
   //Other instance variables omitted.

@property (assign) id<SomeClassDelegate> delegate;
@end

@implementation SomeClass
@synthesize delegate;
- (void) someMethodThatShouldNotifyTheDelegate 
   NSString *hello = @"Hello";
   if (self.delegate != nil && [self.delegate respondsToSelector:@selector(someClassInstance:givesAStringObject:)]) 
      [self.delegate someClassInstance:self givesAStringObject:hello];
   

@end

选项 1 可能是这样的,你必须在对象的 init 中设置变量(当然要实现单例):

#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>

@interface CoreDataUtility : NSObject 
@private
    NSManagedObjectModel *managedObjectModel;
    NSManagedObjectContext *managedObjectContext;
    NSPersistentStoreCoordinator *persistentStoreCoordinator;


+ (CoreDataUtility *)sharedCoreDataUtility;
- (NSEntityDescription *) entityDesctiptionForName:(NSString *)name;
- (NSMutableArray *) executeRequest:(NSFetchRequest *)request;
- (id) getInsertedObjectForEntity:(NSString *)entity;
- (void) deleteAllObjects:(NSString *) entityName;
- (void) deleteManagedObject:(NSManagedObject *)object;
- (void) saveContext;

@end

【讨论】:

您能详细说明一下您提供的选项#1吗? 关于单例的部分是我想知道的。这是单例吗? AV_MonitorAppDelegate *appDelegate = (AV_MonitorAppDelegate *) [[UIApplication sharedApplication] 委托]; 第一:不,不是。它的委托是对实现 UIApplicationDelegate 协议的对象的引用。理论上可以有更多实例,但始终有一组作为您的应用程序的委托。其次:作为程序员,你“知道”这个类实现了你的 CoreData 堆栈。但我认为使用该委托来接近堆栈是不好的做法。它属于 id 类型。这意味着它在运行时动态类型。当您在其上调用选择器但它没有响应时,您的应用程序将在运行时崩溃,并且在编译期间您不会收到警告。 事实上,您编写的那行代码会将委托转换为 AV_MonitorAppDelegate 引用类型。我认为这是错误的,因为您的 AppDelegate 在运行程序时应该是对象层次结构中的最高对象,因此出于设计原因,您不应让其他类对此有任何了解。【参考方案3】:

目前我正在传递一个单一的托管对象上下文,它是 在应用程序中创建委托给每个不同的视图 控制器……这似乎是一种笨拙、笨拙的获得工作的方式 完毕。有没有更好的设计模式可以使用?

在这方面,托管对象上下文没有什么特别之处,它只是您的视图控制器可能需要完成其工作的另一个对象。每当您设置对象来执行任务时,至少可以使用三种策略:

    为对象提供完成工作所需的一切。

    给对象一个助手,它可以用来做决定或获取更多信息。

    将有关应用程序其他部分的足够知识构建到对象中,以便它可以获取所需的信息。

您现在正在做的事情听起来像是第一个策略,我认为这通常是最好的,因为它使您的视图控制器更加灵活,更少依赖于应用程序的其他部分。通过将 MOC 提供给您的视图控制器,您就有可能有一天会在不同的上下文中使用同一个视图控制器。

Jayallengator 进行了有益的观察,即每个托管对象都有对其上下文的引用,如果您正在传递特定的托管对象,您也不需要传递上下文。我会更进一步:如果您将特定的托管对象传递给您的视图控制器,视图控制器通常根本不需要知道上下文。例如,您可能将 Game 对象保存在数据存储中,但 GameBoardViewController 可能只关心正在播放的一个 Game,并且可以使用该对象的接口来获取任何相关对象(Player、Level 等)。也许这些观察可以帮助您简化代码。

第二种策略是委托。当您使用委托时,您通常会使用协议,以便您的对象知道它可以向其助手发送哪些消息,而无需了解有关助手的任何其他信息。委托是一种以有限的、明确定义的方式将必要的依赖项引入代码的方法。例如,UITableView 知道它可以将 UITableViewDelegate 协议中定义的任何消息发送给它的委托,但它不需要知道关于委托的任何其他信息。委托可以是视图控制器,也可以是其他类型的对象;桌子不在乎。表的委托和数据源通常是同一个对象,但不一定是;再次,桌子不在乎。

第三种策略是使用全局变量或共享对象(这是人们谈论单例时通常的意思)。拥有可以从代码中的任何位置访问的共享对象当然很容易,并且您没有配置对象的“笨拙”额外代码行,但这通常意味着您将视图控制器锁定到使用该共享对象而不是其他对象。这很像将锤子粘在手上,因为您确定锤子是您需要的工具。非常适合敲钉子,但如果您后来发现自己想用同一只手拧螺丝或吃晚餐,可能会很痛苦。

【讨论】:

【参考方案4】:

单例方法似乎是最佳实践,但我发现另一个有用的技巧是,如果您将 NSManagedObject 从一个视图控制器传递到下一个视图控制器(通常作为实例变量),您不需要还需要传递 NSManagedObjectContext,因为您可以通过调用 [myManagedObject managedObjectContext] 从传入的对象中获取上下文。当您可能只有一两个方法需要上下文并且您不希望创建另一个 NSManagedObjectContext ivar/property 的开销时,这可能是一个方便的快捷方式。

【讨论】:

单例方法是一种常见的实践,但绝对不是最佳实践。不过,您关于包含对其上下文的引用的托管对象的观点是一个很好的观点。

以上是关于什么是 NSManagedObjectContext 最佳实践?的主要内容,如果未能解决你的问题,请参考以下文章

在 macOS 中初始化 CoreData

如何在核心数据中以一对多关系链接现有值?

什么是帧数?什么是FPS?什么是PING?什么是延迟?什么是延时?什么是延迟时间?什么是时延?.

什么是PP,PE.什么是均聚..什么是共聚..什么是嵌段..什么是无规,什么是注塑。什么是吹膜..什

时间是什么?时间同步是什么?GPS北斗卫星授时又是什么?

什么是拉电流,什么是灌电流?什么是吸收电流 ?