NSOutlineView を設定するためにコア データを取得するためのバックグラウンド スレッドを作成しようとしています。操作には数秒かかることがあるため、操作をバックグラウンド スレッドに入れ、以下に示すようにユーザーに UI を表示したいと考えていました。各セクションの検索が完了すると、メイン スレッドで関数を呼び出して、そのセクションのデータをリロードします。これは正常に動作しているように見えましたが、Core Data フェッチ操作中に例外がスローされたインスタンスが 1 つあります。ユーザーは実際には他に何もできないので、他に何も同時に行うことができない限り、バックグラウンド スレッドで Core Data 呼び出しを行うことはおそらく問題ないと考えました。その後、表示用のデータを保持する配列を更新するコードの周りで NSLock を使用しました。現在、例外を発生させることはできないようです。
私が使用した基本的なアプローチは、以下のコード スニペットに示されています。次のとおりです。 1.initWithNibName
表示用の初期データを入力しますawakeFromNib
。共有オブジェクトの更新中のバックグラウンド スレッドとブロック次のセクションのフェッチのためのバックグラウンド スレッド。
一般に、これは問題なく動作するようであり、メインスレッドですべてを実行して色付きの傘が数秒間回転するのを見るよりも、ユーザーにとってより良い視覚体験を生み出すようです...
これが堅牢なアプローチであるかどうかを誰でも確認できますか? NSLock の機能を完全に理解しているかどうかはわかりません。
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Initialization code here.
_reminderCategories = [NSArray arrayWithObjects:REMINDERS_PAST, REMINDERS_TODAY, REMINDERS_THIS_WEEK, REMINDERS_THIS_MONTH, REMINDERS_THIS_YEAR, nil];
_reminderItems = [NSMutableDictionary new];
[_reminderItems setObject:[NSArray arrayWithObjects:@"Searching...", nil] forKey:REMINDERS_PAST];
[_reminderItems setObject:[NSArray arrayWithObjects:@"Searching...", nil] forKey:REMINDERS_TODAY];
[_reminderItems setObject:[NSArray arrayWithObjects:@"Searching...", nil] forKey:REMINDERS_THIS_WEEK];
[_reminderItems setObject:[NSArray arrayWithObjects:@"Searching...", nil] forKey:REMINDERS_THIS_MONTH];
[_reminderItems setObject:[NSArray arrayWithObjects:@"Searching...", nil] forKey:REMINDERS_THIS_YEAR];
}
return self;
}
- (void)awakeFromNib;
{
// Make sure we only do this once
if (!_awake) {
_awake = YES;
LOG(@"awakeFromNib called !");
//set the start menu option
[_sidebarOutlineView sizeLastColumnToFit];
[_sidebarOutlineView reloadData];
[_sidebarOutlineView setFloatsGroupRows:NO];
// Expand all the root items; disable the expansion animation that normally happens
[NSAnimationContext beginGrouping];
[[NSAnimationContext currentContext] setDuration:0];
[_sidebarOutlineView expandItem:nil expandChildren:YES];
[NSAnimationContext endGrouping];
if (!_searched) {
_searched=YES;
[self getPastRemindersUsingThread];
}
}
}
- (void)getPastRemindersUsingThread
{ //LOG(@"getPastRemindersUsingThread called");
_loading = YES;
[self performSelectorInBackground:@selector(threadGetPastReminders) withObject:nil];
}
- (void)threadGetPastReminders
{
//LOG(@"threadGetReminders called");
assert( ! [NSThread isMainThread] );
[NSThread sleepForTimeInterval:2]; // Delays for testing
[self makeMenus];
[self performSelectorOnMainThread:@selector(pastRemindersDone) withObject:nil waitUntilDone:NO];
}
- (void)pastRemindersDone {
//LOG(@"pastRemindersDone called");
[_sidebarOutlineView reloadData];
[self getTodaysRemindersUsingThread];
}
- (void)getTodaysRemindersUsingThread
{ //LOG(@"getTodaysRemindersUsingThread called");
_loading = YES;
[self performSelectorInBackground:@selector(threadGetReminders) withObject:nil];
}
- (void)threadGetReminders
{
//LOG(@"threadGetReminders called");
assert( ! [NSThread isMainThread] );
[self makeTodaysMenusX];
[self performSelectorOnMainThread:@selector(todaysRemindersDone) withObject:nil waitUntilDone:NO];
}
- (void)todaysRemindersDone {
//LOG(@"todaysRemindersDone called");
[_sidebarOutlineView reloadData];
[self getThisWeeksRemindersUsingThread];
}
// etc…
-(void)makeMenus {
//LOG(@"makeMenus called");
// Make sure nothing else is going on while we execute this code
NSLock *theLock = [[NSLock alloc] init];
if ([theLock tryLock]) {
[self makeDates];
[self getAllReminders];
[self makePastMenus];
}
[theLock unlock];
}
- (void)makePastMenus {
//LOG(@"makePastMenus called");
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"nextReminderDate < %@",_todayStartDate];
[self makeMenu:REMINDERS_PAST predicate:predicate];
}
- (void)makeTodaysMenus {…}
- (void)makeThisWeeksMenus {…}
- (void)makeThisYearsMenus {…}
// Use this to get the subset that fall within the specified dates (in the predicate)
// nextReminderDate is calculated, and not an attribute stored in the database
- (void)makeMenu:(NSString*)menu predicate:(NSPredicate*)predicate {
LOG(@"makeMenuX called");
NSLock *theLock = [[NSLock alloc] init];
if ([theLock tryLock]) {
if (_yearlyReminders) {
NSMutableArray *reminders = [[NSMutableArray alloc] init];
[reminders addObjectsFromArray:[_yearlyReminders filteredArrayUsingPredicate:predicate]];
NSSortDescriptor *indexSort = [[NSSortDescriptor alloc] initWithKey:@"nextReminderDate" ascending:YES];
NSArray *sorters = [NSArray arrayWithObject:indexSort]; indexSort = nil;
[_reminderItems setObject:[reminders sortedArrayUsingDescriptors:sorters] forKey:menu];
} else {
LOG(@" error utilities is nil!");
}
}
[theLock unlock];
LOG(@"makePastYearsMenus finished");
}
// Get all the reminders from the Core Data store
// _utilities getData is a helper function that performs the Core Data fetch and returns an array of NSManagedObjects
- (void)getAllReminders {
//LOG(@"getYearlyReminders called");
NSLock *theLock = [[NSLock alloc] init];
if ([theLock tryLock]) {
if (_utilities) {
NSPredicate *predicate = [...];
NSMutableArray *reminders = [[NSMutableArray alloc] init];
[reminders addObjectsFromArray:[_utilities getData:@"Entity1" sortKey:@"reminderDate" predicate:predicate]];
[reminders addObjectsFromArray:[_utilities getData:@"Entity2" sortKey:@"reminderDate" predicate:predicate]];
[reminders addObjectsFromArray:[_utilities getData:@"Entity3" sortKey:@"reminderDate" predicate:predicate]];
[reminders addObjectsFromArray:[_utilities getData:@"Entity4" sortKey:@"reminderDate" predicate:predicate]];
NSSortDescriptor *indexSort = [[NSSortDescriptor alloc] initWithKey:@"nextReminderDate" ascending:YES];
NSArray *sorters = [NSArray arrayWithObject:indexSort]; indexSort = nil;
_yearlyReminders = [[NSArray alloc] initWithArray:[reminders sortedArrayUsingDescriptors:sorters]];
} else {
LOG(@" error utilities is nil!");
}
}
[theLock unlock];
}