我正在使用基于导航控制器的iOS应用。有多个tableView屏幕可以从Core Data持久性存储中提取和保存数据。不同表视图的大多数数据来自NSFetchedResultsController实例或NSFetchRequests。
该应用程序按预期工作,但我收到了一些似乎与Core Data相关的随机崩溃和故障。例如,有时当我保存上下文时,应用程序会崩溃,但并非总是如此。我一直看到的另一件事是第一个tableView并不总是更新反映在其详细视图中修改的数据。
目前,我正在通过在将其推送到导航堆栈之前设置视图控制器的上下文属性,将应用程序委托中创建的单个托管对象上下文传递给每个不同的视图控制器。
这似乎是一种笨重,笨拙的完成工作的方式。是否有更好的设计模式可供使用?
我在其中一个使用委托的WWDC会话中注意到,但我以前从未使用过创建自己的代理,也无法将其从WWDC会话中解脱出来。
感谢。
=)
答案 0 :(得分:9)
对所有控制器使用singleton NSManagedObjectContext不是最佳做法。
每个Controller都应该有自己的Context来管理文档存储中的特定(有时是原子)操作。
想想你是否可以编辑一个附加到Controller的NSManagedObject,它将相同的Context传递给另一个将选择另一个要删除或编辑的实例的控制器..你可能会丢失有关修改状态的控件。
创建视图控制器时,会向其传递上下文。你通过了 现有上下文,或(在您需要新控制器的情况下) 管理一组离散的编辑)您为其创建的新上下文 它。它通常是应用程序委托的责任 创建一个上下文传递给第一个视图控制器 显示。
答案 1 :(得分:4)
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中设置变量(并实现单例ofcourse):
#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
答案 2 :(得分:3)
目前我正在传递一个托管对象上下文 在app中创建委托给每个不同的视图 控制器......这似乎是一种笨重,笨拙的方式来获得这份工作 完成。是否有更好的设计模式可供使用?
在这方面,托管对象上下文没有什么特别之处,它只是视图控制器可能需要完成其工作的另一个对象。每当您设置一个对象来执行任务时,您至少可以使用三种策略:
为对象提供完成工作所需的一切。
为对象提供一个帮助程序,可用于制定决策或获取其他信息。
将对应用程序其他部分的足够知识构建到可以获取所需信息的对象中。
你现在正在做的事情听起来像是第一个策略,我认为它通常是最好的,因为它使你的视图控制器更灵活,更少依赖于应用程序的其他部分。通过向视图控制器提供MOC,您可能有一天可能会使用具有不同上下文的相同视图控制器。
Jayallengator提供了有用的观察结果,即每个托管对象都有对其上下文的引用,如果您传递特定的托管对象,则不需要传递上下文。我更进一步:如果您将特定的托管对象传递给视图控制器,视图控制器通常根本不需要知道上下文。例如,您可能将Game对象保留在数据存储中,但GameBoardViewController可能只关心正在播放的一个Game,并且可以使用该对象的接口来获取任何相关对象(Player,Level等)。也许这些观察可以帮助您简化代码。
第二个策略是授权。当您使用委托时,通常会使用协议,以便您的对象知道它可以发送其帮助程序的消息,而无需了解有关帮助程序的任何其他信息。委派是一种以有限的,明确定义的方式在代码中引入必要依赖的方法。例如,UITableView知道它可以将UITableViewDelegate协议中定义的任何消息发送给它的委托,但是它不需要知道委托的任何其他内容。委托可以是视图控制器,也可以是其他类型的对象;桌子不在乎。表的委托和数据源通常是同一个对象,但它们不一定是;再次,表不关心。
第三种策略是使用全局变量或共享对象(这是人们谈论单身时通常所说的)。拥有一个可以从代码中的任何位置访问的共享对象当然很容易,并且您没有配置对象的“klunky”额外代码行,但它通常意味着您将视图控制器锁定到使用该共享对象而不是其他。这很像是将锤子粘在手上,因为你肯定知道那把锤子是你需要的工具。适用于敲击指甲,但如果你后来发现你想用同一只手来驱动螺丝或吃晚餐,那就太痛苦了。
答案 3 :(得分:1)
单例方法似乎是最佳实践,但我发现有用的另一个技巧是,在你将NSManagedObject从一个视图控制器传递到另一个视图控制器的情况下(通常作为实例变量),你不会还需要传递NSManagedObjectContext,因为您可以通过调用[myManagedObject managedObjectContext]从传入的对象中获取上下文。当可能只有一两个方法需要上下文并且您不希望创建另一个NSManagedObjectContext ivar / property的开销时,这可以是一个方便的快捷方式。