Ios 拉刷新应用程序

Ios 拉刷新应用程序,ios,uitableview,ios7,pull-to-refresh,Ios,Uitableview,Ios7,Pull To Refresh,我有一个应用程序,它从3个共享的谷歌日历中获取事件,并将其显示在表视图中 我想实现拉入刷新,但如果我在加载数据之前放弃拉入,应用程序会不断崩溃。(如果我保持拉力几秒钟,一切都很好——如果我立即松开,它就会崩溃 代码: 它在CellForRowatineXpath方法的这一行中给出了一个SIGABRT错误: NSInteger index = [self getRow:sortedStartDates[indexPath.section]]; // get correct index for

我有一个应用程序,它从3个共享的谷歌日历中获取事件,并将其显示在表视图中

我想实现拉入刷新,但如果我在加载数据之前放弃拉入,应用程序会不断崩溃。(如果我保持拉力几秒钟,一切都很好——如果我立即松开,它就会崩溃

代码:

它在CellForRowatineXpath方法的这一行中给出了一个SIGABRT错误:

 NSInteger index = [self getRow:sortedStartDates[indexPath.section]];  // get correct index for sectionEntries
错误:*由于未捕获的异常“NSRangeException”而终止应用程序,原因:“*-[\uu NSArrayI objectAtIndex:]:索引4超出空数组的界限”


错误似乎是因为我的startDates NSMutableArray中没有数据,但是如果我注释行[startDates removeAllObjects]我有多余的单元格。

至少,我建议检查以确保刷新尚未进行。您可能还希望更改
getEvents
以将刷新控件作为参数,并相应地更新下拉列表(以便用户知道刷新正在进行):


但是,在异步更新模型数据时应该非常小心(因为在更新过程中,主队列可能会尝试从模型中检索信息)你真的应该推迟模型的更新直到最后的调度到主队列。但是不要在异步进程中间更新模型,否则你的模型和UI会暂时处于不一致的状态。 此外,作为一种改进,您可能希望同时检索这三个数据源,并且可以观察到明显的性能改进

- (void)getEvents:(UIRefreshControl *)refresh
{
    static BOOL refreshInProgress = NO;

    if (!refreshInProgress)
    {
        refreshInProgress = YES;

        refresh.attributedTitle = [[NSAttributedString alloc] initWithString:@"Refreshing"]; // let the user know refresh is in progress

        // get the data here

        __block NSData *data1 = nil;
        __block NSData *data2 = nil;
        __block NSData *data3 = nil;

        dispatch_queue_t queue = dispatch_queue_create([[[[NSBundle mainBundle] bundleIdentifier] stringByAppendingString:@".network"] UTF8String], DISPATCH_QUEUE_CONCURRENT);

        dispatch_async(queue, ^{
            data1 = [NSData dataWithContentsOfURL:sportsCalendarURL];
        });

        dispatch_async(queue, ^{
            data2 = [NSData dataWithContentsOfURL:musicCalendarURL];
        });

        dispatch_async(queue, ^{
            data3 = [NSData dataWithContentsOfURL:fixedCalendarURL];
        });

        // use dispatch barrier here, which will only fire when the previous three requests are done

        dispatch_barrier_async(queue, ^{

            // update the UI here

            dispatch_async(dispatch_get_main_queue(), ^{

                startDates     = [NSMutableArray array];
                sectionEntries = [NSMutableArray array];
                entries        = [NSMutableArray array];

                [self fetchedData:data1];
                [self fetchedData:data2];
                [self fetchedData:data3];

                refresh.attributedTitle = [[NSAttributedString alloc] initWithString:@"Pull to Refresh"]; // reset the message
                [refresh endRefreshing];

                sortedStartDates = [startDates sortedArrayUsingSelector:@selector(compare:)];
                [self.tableView reloadData];

                refreshInProgress = NO;
            });
        });
    }
}

如果你只有三个数据源,你可能可以摆脱GCD并发队列,但是如果你有更多的数据队列,你可能需要使用一个操作队列来约束并发请求的数量。你可能会在其他地方同时进行

但这里的主要观察结果是(a)在刷新完成并准备好更新UI之前不要更新模型;以及(b)确保在上一次刷新正在进行时不要启动新的刷新(或者,如果您确实需要它,请转到操作队列模型,在该模型中,您可以创建可取消的
NSOperation
子类,然后理论上可以在发出另一个更新请求之前取消先前的请求(如果有)


与手头的问题完全无关,但在我的第一个代码片段中,您将看到我将
\u imageForCalendarType
的设置从该块中移出(因为您总是将其设置为相同的内容)并移至
viewDidLoad
。我还删除了这一不必要的行:

_imageForCalendarType = [[NSDictionary alloc]init];
您将在下一行中为字典文本放弃此实例化字典,因此不需要上一行


坦白地说,你甚至不应该有一个
UIImage
对象的字典,而应该有一个图像名称的字典,并且在那里有
cellforrowatinexpath
实例化
UIImage
。当你只有三个图像时,这可能并不重要,但是如果你有更多的图像,现有的
UIIm数组年龄
对象构造在内存紧张的情况下可能会出现问题。是的,您可以插入适当的
didReceiveMemoryWarning
处理,但从一开始就不使用
UIImage
对象维护字典要简单得多。

至少,我建议检查以确保刷新是正确的尚未进行。您可能还希望更改
getEvents
以将刷新控件作为参数,并相应地更新下拉列表(以便用户知道正在进行刷新):


但是,在异步更新模型数据时应该非常小心(因为在更新过程中,主队列可能会尝试从模型中检索信息)你真的应该推迟模型的更新直到最后的调度到主队列。但是不要在异步进程中间更新模型,否则你的模型和UI会暂时处于不一致的状态。 此外,作为一种改进,您可能希望同时检索这三个数据源,并且可以观察到明显的性能改进

- (void)getEvents:(UIRefreshControl *)refresh
{
    static BOOL refreshInProgress = NO;

    if (!refreshInProgress)
    {
        refreshInProgress = YES;

        refresh.attributedTitle = [[NSAttributedString alloc] initWithString:@"Refreshing"]; // let the user know refresh is in progress

        // get the data here

        __block NSData *data1 = nil;
        __block NSData *data2 = nil;
        __block NSData *data3 = nil;

        dispatch_queue_t queue = dispatch_queue_create([[[[NSBundle mainBundle] bundleIdentifier] stringByAppendingString:@".network"] UTF8String], DISPATCH_QUEUE_CONCURRENT);

        dispatch_async(queue, ^{
            data1 = [NSData dataWithContentsOfURL:sportsCalendarURL];
        });

        dispatch_async(queue, ^{
            data2 = [NSData dataWithContentsOfURL:musicCalendarURL];
        });

        dispatch_async(queue, ^{
            data3 = [NSData dataWithContentsOfURL:fixedCalendarURL];
        });

        // use dispatch barrier here, which will only fire when the previous three requests are done

        dispatch_barrier_async(queue, ^{

            // update the UI here

            dispatch_async(dispatch_get_main_queue(), ^{

                startDates     = [NSMutableArray array];
                sectionEntries = [NSMutableArray array];
                entries        = [NSMutableArray array];

                [self fetchedData:data1];
                [self fetchedData:data2];
                [self fetchedData:data3];

                refresh.attributedTitle = [[NSAttributedString alloc] initWithString:@"Pull to Refresh"]; // reset the message
                [refresh endRefreshing];

                sortedStartDates = [startDates sortedArrayUsingSelector:@selector(compare:)];
                [self.tableView reloadData];

                refreshInProgress = NO;
            });
        });
    }
}

如果你只有三个数据源,你可能可以摆脱GCD并发队列,但是如果你有更多的数据队列,你可能需要使用一个操作队列来约束并发请求的数量。你可能会在其他地方同时进行

但这里的主要观察结果是(a)在刷新完成并准备好更新UI之前不要更新模型;以及(b)确保在上一次刷新正在进行时不要启动新的刷新(或者,如果您确实需要它,请转到操作队列模型,在该模型中,您可以创建可取消的
NSOperation
子类,然后理论上可以在发出另一个更新请求之前取消先前的请求(如果有)


与手头的问题完全无关,但在我的第一个代码片段中,您将看到我将
\u imageForCalendarType
的设置从该块中移出(因为您总是将其设置为相同的内容)并移至
viewDidLoad
。我还删除了这一不必要的行:

_imageForCalendarType = [[NSDictionary alloc]init];
您将在下一行中为字典文本放弃此实例化字典,因此不需要上一行

坦率地说,您甚至不应该有一个
UIImage
对象的字典,而应该有一个图像名称的字典,并且有
cellforrowatinexpath
instanti