用于填充NSTableView的后台线程的最佳设计模式

时间:2013-09-17 03:05:11

标签: objective-c multithreading thread-safety nslock

我正在尝试创建后台线程以获取Core Data以填充NSOutlineView。操作可能需要几秒钟,因此我想将操作放在后台线程中并向用户显示UI,如下所示。当搜索完成每个部分时,我在主线程上调用一个函数来重新加载该部分的数据。这似乎工作正常,但我有一个实例,在Core Data fetch操作期间抛出异常。用户实际上无法做任何其他事情,所以我认为在后台线程上进行Core Data调用可能没问题,只要没有其他事情可以同时执行。随后我使用NSLock围绕代码对包含显示数据的数组进行更新。我现在似乎无法引起任何例外。这是从managedObjectContext检索数据的安全方法吗?

enter image description here

我使用的基本方法在下面的代码片段中说明,如下所示: 1.在initWithNibName中填充初始数据以供显示 2.在awakeFromNib设置后台线程的outlineView和kick以获取第一组数据 3.在更新共享对象时运行后台线程和块 4.当线程完成时,触发主线程上的函数执行,以a)重新加载outlineView(希望我知道如何仅重新加载NSOutlineView的部分),以及b)后台线程的踢,用于下一部分的获取。

一般情况下,这似乎工作正常,似乎为用户创造了更好的视觉体验,而不仅仅是在主线上运行它并观看彩色伞旋转几秒钟......

任何人都可以确认这是否是一种强有力的方法 - 我不确定我是否完全理解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];
}

0 个答案:

没有答案