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

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

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

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

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

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

谢谢。

=)

【问题讨论】:

    标签: objective-c ios core-data


    【解决方案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】:

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

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

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

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

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

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

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

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

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

      【讨论】:

        【解决方案4】:

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

        【讨论】:

        • 单例方法是一种常见的实践,但绝对不是最佳实践。不过,您关于包含对其上下文的引用的托管对象的观点是一个很好的观点。
        猜你喜欢
        • 2011-06-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-08-22
        • 2011-03-04
        • 2019-01-25
        相关资源
        最近更新 更多