managedObjectContext为nil或fetch无效

时间:2014-03-11 20:08:35

标签: objective-c cocoa core-data nsmanagedobjectcontext

我有一个由多个视图构成的应用程序,我处理Core Data实体,通常使用NSTableViews以最简单和通常的方式使用绑定连接到数组控制器。但是其中一个视图不使用常规绑定,因为必须处理此实体的对象(一些字符串用于生成其他字符串)并在基于视图的NSTableView中显示,因此它们需要一个中间数组来根据我的需要打包它们。在图形界面上,我使用带有“objectValue:key_name”的绑定(来自新数组的键由处理过的数据组成)。

使用我的应用程序的其他视图和实体,当我单击工具栏图标时,会显示一个特定的视图,并且由于绑定,对象图的修改会自动显示在表视图中。

当我显示与特定处理相关的视图时,更新显然不会立即可见,我必须启动一个(void)函数,以编程方式执行新的获取,处理结果并更新填充基于视图的表的数组图。

问题是:在应用程序启动时正确填充了表视图,因为我在awakeFromNib中调用了该函数。如果我想更新表视图,我可以通过单击连接到调用更新功能的IBAction的按钮来完成。它也有效。但是当我使用工具栏图标返回视图时,它不起作用,虽然调用此视图也调用了更新函数(意味着在AppDelegate类中,我正在调用在特定视图控制器类中编写的函数) )。

首先,它崩溃,调试器显示managedObjectContext为零。这是一个惊喜,因为它可以在从awakeFromNib或IBAction调用函数时正常工作。在网上搜索解释,我发现我必须明确地引用应用程序的托管对象上下文。所以我为此添加了代码,现在它不再崩溃,但它什么也没做。从awakeFormNib或IBAction调用时,该函数仍然可以正常工作,但是当视图管理器通过我的工具栏单击调用时,该函数无效。

我错过了什么?谢谢你的帮助。以下是特定视图控制器的代码:

//////// OpplogViewController.h

#import <Cocoa/Cocoa.h>
#import "ManagingViewController.h"
@interface OpplogViewController : ManagingViewController {
IBOutlet NSTableView *opplogTableView;
IBOutlet NSArrayController *opplogController;
}
@property (strong) NSMutableArray *allOpps;
- (IBAction)refreshOpp:(id)sender;
- (void)fetchTasks:(NSString *)entityName;
@end



//////// OpplogViewController.m

#import "OpplogViewController.h"
@interface OpplogViewController ()
@end

@implementation OpplogViewController
@synthesize allOpps = _allOpps;
- (id)init {
    self = [super initWithNibName:@"OpplogViewController" bundle:nil];
    if (!self) {
        return nil;
    }
    [self setTitle:NSLocalizedString(@"Opplogview", @"")];
    _allOpps = [[NSMutableArray alloc] init];
    return self;
}

- (void)awakeFromNib {
    [self fetchTasks:@"Opplog"];
    // Opplog is the database entity to be fetched
}

- (IBAction)refreshOpp:(id)sender {
    [self fetchTasks:@"Opplog"];
}

// below is the function called to fetch data and update the table view
- (void)fetchTasks:(NSString *)entityName {
    // below: 2 lines added to avoid crash because context is nil
    id delegate = [[NSApplication sharedApplication] delegate];
    self.managedObjectContext = [delegate managedObjectContext];
    // below, we reset the array used to populate the table view
    [_allOpps removeAllObjects];
    NSFetchRequest *requestOpp = [[NSFetchRequest alloc] init];
    NSPredicate *predicate = ...some predicate... ;
    [requestOpp setEntity:[NSEntityDescription entityForName:entityName inManagedObjectContext:self.managedObjectContext]];
    [requestOpp setPredicate:predicate];
    NSError *error = nil;
    NSArray *fetchResult = [self.managedObjectContext executeFetchRequest:requestOpp error:&error];
    for (id obj in fetchResult) {
        // processing the data
        // creating a dictionary with resulting data
        NSDictionary *anOpp = ... ;
        // populating the array with the dictionaries
        [_allOpps addObject:anOpp];
    }
[_allOpps sortUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"date" ascending:YES]]];
[opplogController setContent:_allOpps];
[opplogTableView reloadData];
}

@end

这是AppDelegate,如果我们回到那个特定的视图,它会调用Opplog视图控制器类中的更新函数:

////// AppDelegate.h
#import <Cocoa/Cocoa.h>
@class ManagingViewController;
@interface AppDelegate : NSObject <NSApplicationDelegate> {
    NSMutableArray *viewControllers;
    IBOutlet NSToolbar *toolBar;
}
@property (unsafe_unretained) IBOutlet NSWindow *window;
@property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;
@property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
@property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
- (IBAction)changeViewController:(id)sender;
- (void)displayViewController:(ManagingViewController *)vc;


////// AppDelegate.m
#import "AppDelegate.h"
#import "OpplogViewController.h"
// also importing the controller class header of the app's other views
// ...
@implementation AppDelegate
@synthesize persistentStoreCoordinator = _persistentStoreCoordinator;
@synthesize managedObjectModel = _managedObjectModel;
@synthesize managedObjectContext = _managedObjectContext;

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// nothing here, was generated by Xcode
}

-(id)init {
    self = [super init];
    viewControllers = [[NSMutableArray alloc] init];
    ManagingViewController *vc;

    vc = [[OpplogViewController alloc] init];
    [vc setManagedObjectContext:[self managedObjectContext]];
    [viewControllers addObject:vc];
    // below, repeating the same as paragraph above, for each view of the app
    // ...

    return self;
}

-(void)awakeFromNib {
    [self displayViewController:viewControllers[0]];
    [toolBar setSelectedItemIdentifier:NSLocalizedString(@"ViewOpplog", @"")];
    // because the Opplog view is the view which must be displayed on startup
}

-(void)displayViewController:(ManagingViewController *)vc {
    BOOL ended = [_window makeFirstResponder:_window];
    if (!ended) {
        NSBeep();
        return;
    }
    NSView *v = [vc view];
    [_window setContentView:v];
}

-(IBAction)changeViewController:(id)sender {
    // switching views is an opportunity to save the context
    NSError *error = nil;
    [[self managedObjectContext] save:&error];
    // now managing the view switch using toolbar icons
    // the sender is the clicked toolbar item
    NSInteger i = [sender tag];
    ManagingViewController *vc = viewControllers[i];

    // We go back to the Opplog view using the first toolbar icon
    // which has a tag=0 and we call the function that gives us trouble.
    // This call is supposed to update the data on the Opplog view
    // when it will be loaded.
    if (i == 0) {
        OpplogViewController *pendingController = [[OpplogViewController alloc] init];
        [pendingController fetchTasks:@"Opplog"];
    }
    [self displayViewController:vc];
}

这是ManagingViewController类的代码。

////// ManagingViewController.h
#import <Cocoa/Cocoa.h>
@interface ManagingViewController : NSViewController {
    NSManagedObjectContext *__strong managedObjectContext;
}
@property (strong) NSManagedObjectContext *managedObjectContext;
@end


////// ManagingViewController.m
#import "ManagingViewController.h"
@interface ManagingViewController ()
@end

@implementation ManagingViewController
@synthesize managedObjectContext;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
return self;
}
@end

1 个答案:

答案 0 :(得分:0)

对象的发送顺序-awakeFromNib未定义,因此在Core Data堆栈可用之前,您的-awakeFromNib消息可能会被发送。您可能希望使用另一种方法,例如在设置视图控制器的表示对象时,或者在调用应用程序委托的-applicationDidFinishLaunching:时告诉它刷新(并且堆栈肯定可用)

根据评论进行修改

在您的-changeViewController方法中,您需要重新分配/初始化OpplogViewController的新实例,然后要求其获取。看起来您只想切换回索引0处的视图控制器,这是您最初创建的OpplogViewController实例。在这种情况下,我猜你想说:

if (i == 0)
  [(OppLogViewController *)vc fetchTasks:@"Opplog"];

您不想创建新实例并对其进行消息传递,而是立即泄漏它,而是要发送您最初创建并保留的实例。换句话说,你告诉错误的人去取。

一些其他提示:请注意我提到的有关-awakeFromNib-init的内容,因为理解这一点至关重要。来自NSNibAwaking Protocol文档:

  

由于无法保证从归档实例化对象的顺序,因此初始化方法不应将消息发送到层次结构中的其他对象。可以从awakeFromNib中安全地发送到其他对象的消息 - 这时确保所有对象都被取消存档并初始化(当然,不一定被唤醒)。