我有一个由多个视图构成的应用程序,我处理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
答案 0 :(得分:0)
对象的发送顺序-awakeFromNib未定义,因此在Core Data堆栈可用之前,您的-awakeFromNib消息可能会被发送。您可能希望使用另一种方法,例如在设置视图控制器的表示对象时,或者在调用应用程序委托的-applicationDidFinishLaunching:时告诉它刷新(并且堆栈肯定可用)
根据评论进行修改
在您的-changeViewController
方法中,您需要重新分配/初始化OpplogViewController
的新实例,然后要求其获取。看起来您只想切换回索引0处的视图控制器,这是您最初创建的OpplogViewController
实例。在这种情况下,我猜你想说:
if (i == 0)
[(OppLogViewController *)vc fetchTasks:@"Opplog"];
您不想创建新实例并对其进行消息传递,而是立即泄漏它,而是要发送您最初创建并保留的实例。换句话说,你告诉错误的人去取。
一些其他提示:请注意我提到的有关-awakeFromNib
和-init
的内容,因为理解这一点至关重要。来自NSNibAwaking Protocol文档:
由于无法保证从归档实例化对象的顺序,因此初始化方法不应将消息发送到层次结构中的其他对象。可以从awakeFromNib中安全地发送到其他对象的消息 - 这时确保所有对象都被取消存档并初始化(当然,不一定被唤醒)。