【问题标题】:Asynchronous loading and UITableView's -reloadData异步加载和 UITableView 的 -reloadData
【发布时间】:2013-10-28 22:29:48
【问题描述】:

我目前试图弄清楚为什么以下代码 sn-p 会产生错误。我正在使用 Google Calendar API 加载日历。但是我假设它异步加载其数据,因此当我调用 -reloadData 我的应用程序崩溃时没有控制台错误。

[[GDataHandler sharedDataHandler] fetchCalendarsWithCompletion:^(GDataFeedBase *feed) {


     //CRASH !!! Trying to execute code on main thread
     dispatch_async(dispatch_get_main_queue(), ^{

        for(GDataEntryCalendar *cal in feed.entries){

            if([cal isKindOfClass:[GDataEntryCalendar class]]){
                NSLog(@"adding %@", cal.title.stringValue);
                [calendarArray addObject:cal];
            }

        }

        [list reloadData];

    });

}];

回溯看起来像这样:

* thread #1: tid = 0xeabe5, 0x39ed81fc libsystem_kernel.dylib`__pthread_kill + 8, queue = 'com.apple.main-thread, stop reason = signal SIGABRT
    frame #0: 0x39ed81fc libsystem_kernel.dylib`__pthread_kill + 8
    frame #1: 0x39f3fa52 libsystem_pthread.dylib`pthread_kill + 58
    frame #2: 0x39e8902c libsystem_c.dylib`abort + 76
    frame #3: 0x392d798e libc++abi.dylib`abort_message + 74
    frame #4: 0x392f06e6 libc++abi.dylib`default_terminate_handler() + 254
    frame #5: 0x39928938 libobjc.A.dylib`_objc_terminate() + 192
    frame #6: 0x392ee1b2 libc++abi.dylib`std::__terminate(void (*)()) + 78
    frame #7: 0x392edd16 libc++abi.dylib`__cxa_rethrow + 102
    frame #8: 0x3992880e libobjc.A.dylib`objc_exception_rethrow + 42
    frame #9: 0x2f5615b6 CoreFoundation`CFRunLoopRunSpecific + 642
    frame #10: 0x2f561322 CoreFoundation`CFRunLoopRunInMode + 106
    frame #11: 0x342982ea GraphicsServices`GSEventRunModal + 138
    frame #12: 0x31e181e4 UIKit`UIApplicationMain + 1136
    frame #13: 0x000b8e44 MyApp`main(argc=1, argv=0x27d54d0c) + 116 at main.m:16
    frame #14: 0x39e21ab6 libdyld.dylib`start + 2

这里是委托方法:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{

    return calendarArray.count;

}

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{


    static NSString *cellId = @"CellIdentifier";
    CustomListCell *cell = [tableView dequeueReusableCellWithIdentifier:cellId];

    if(cell == nil){

        cell = [[CustomListCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellId];

    }

    cell.calendar = [calendarArray objectAtIndex:indexPath.row];

    return cell;

}

【问题讨论】:

  • 请发布您的 UITableViewDelegate 方法,它们可能是原因。
  • @Muhammad 添加了委托方法 :-)
  • 旁注:在-tableView:cellForRowAtIndexPath: 方法之前发生崩溃...
  • 我担心,崩溃的原因是您代码中未显示的其他地方。否则显示的代码对我来说看起来不错。您需要提供更多信息,例如有关异常的更多信息(在 Xcode 中添加异常断点),以及 feed 的属性 entries 到底是什么。问题的另一个潜在来源是完成处理程序在底层任务仍在运行时被错误地调用。
  • 你的委托方法看起来没问题,崩溃发生在[list reloadData],这很奇怪,你确定你安全地通过了for循环吗?

标签: ios objective-c uitableview asynchronous gdata-api


【解决方案1】:

这里有两个问题。

首先,当您调用dispatch_get_main_queue() 时,您实际上会返回主线程,这对于后台处理来说并不理想。

所以你需要把它改成:

dispatch_queue_t concurrentQueue = dispatch_queue_create("MyQueue", NULL);
dispatch_async(concurrentQueue, ^{
    ... process your data here ...
});

第二个问题是你不能从后台线程调用UITableView#reloadData。所以你实际上只需要在主线程上安排一个:

dispatch_async(dispatch_get_main_queue(), ^{
    [list reloadData];
});

reloadData 的上述代码将进入您处理数据的块中,因此 fetchCalendarsWithCompletion 的完成块将类似于:

dispatch_queue_t concurrentQueue = dispatch_queue_create("MyQueue", NULL);
dispatch_async(concurrentQueue, ^{
    ... process your data here ...
    dispatch_async(dispatch_get_main_queue(), ^{
        [list reloadData];
    });
});

【讨论】:

  • 我不太确定我是否正确理解了你的答案...我知道-reloadData不能从后台队列中调用,但整个块在后台队列中执行,即为什么(在我的问题中)我围绕主队列的调度块。
  • 这个答案更多的是性能优化,并没有专门解决这个问题。
【解决方案2】:

你应该得到一个控制台日志,类似于:

** Terminating app due to uncaught exception ...

堆栈跟踪告诉我们,在运行循环中已经抛出并捕获了一个异常。然后运行循环只能重新抛出它。异常发生在您的完成块中的某处。

为了捕获异常,您可以在符号objc_exception_throw 处设置符号断点,或者使用Xcode 的UI 设置异常断点:Adding an Exception Breakpoint

我怀疑,在您迭代数组时,另一个线程正在更改数组。

【讨论】:

  • 我将添加几个断点并返回报告...但是,我很确定在迭代发生时数组没有被更改,因为这是唯一使用 calendarArray 的地方,并且完成处理程序只执行一次。所以这确实很奇怪。
猜你喜欢
  • 2013-04-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多