为什么UITableView会频繁调用其代理和数据源?

4

有人能解释一下为什么UITableView在设置时会多次调用其委托和数据源吗?看着我现在正在处理的一个表格,我发现numberOfSectionsInTableView被调用了3次,然后viewForHeaderInSection又循环了3次来处理每个部分(我有2个部分,所以在第一次显示之前总共6次)...所有这些都是在第一个屏幕渲染之前完成的。

我知道这个API是私有的,但想知道是否有人愿意提供一些关于这种设计模式的见解,以及背后可能发生了什么,这样我就可以学习一些关于为什么这么多重复被认为是可接受的(甚至是有利的)的东西...或者更有可能的是,我如何搞砸了它的应用。

编辑:在RootViewController tableView首次显示之前添加堆栈跟踪

第一次调用viewForHeaderInSection:

#0  0x000086cc in -[RootViewController tableView:viewForHeaderInSection:] at RootViewController.m:444
#1  0x30c901a8 in -[UITableView(UITableViewInternal) _delegateWantsHeaderForSection:]
#2  0x30c8f6f4 in -[UISectionRowData refreshWithSection:tableView:tableViewRowData:]
#3  0x30c8f5a8 in -[UITableViewRowData rectForFooterInSection:]
#4  0x30c3d430 in -[UITableViewRowData heightForTable]
#5  0x30c05430 in -[UITableView(_UITableViewPrivate) _updateContentSize]
#6  0x30c3ce0c in -[UITableView noteNumberOfRowsChanged]
#7  0x30c3c7c0 in -[UITableView reloadData]
#8  0x30e50e60 in -[UITableViewController viewWillAppear:]
#9  0x30c78810 in -[UINavigationController _startTransition:fromViewController:toViewController:]
#10 0x30c783b0 in -[UINavigationController _startDeferredTransitionIfNeeded]
#11 0x30c782a0 in -[UINavigationController viewWillLayoutSubviews]
#12 0x30c5c874 in -[UILayoutContainerView layoutSubviews]
#13 0x30c382d8 in -[UIView(CALayerDelegate) _layoutSublayersOfLayer:]
#14 0x33e871c0 in -[CALayer layoutSublayers]
#15 0x33e86edc in CALayerLayoutIfNeeded
#16 0x33e86844 in CA::Context::commit_transaction
#17 0x33e86474 in CA::Transaction::commit
#18 0x33e8e5dc in CA::Transaction::observer_callback
#19 0x32d5c830 in __CFRunLoopDoObservers
#20 0x32da4346 in CFRunLoopRunSpecific
#21 0x32da3c1e in CFRunLoopRunInMode
#22 0x31bb9374 in GSEventRunModal
#23 0x30bf3c30 in -[UIApplication _run]
#24 0x30bf2230 in UIApplicationMain
#25 0x00002260 in main at main.m:14

2nd call:

#0  0x000086cc in -[RootViewController tableView:viewForHeaderInSection:] at RootViewController.m:444
#1  0x30c901a8 in -[UITableView(UITableViewInternal) _delegateWantsHeaderForSection:]
#2  0x30c8f6f4 in -[UISectionRowData refreshWithSection:tableView:tableViewRowData:]
#3  0x30c90628 in -[UITableViewRowData(UITableViewRowDataPrivate) _ensureSectionOffsetIsValidForSection:]
#4  0x30c8f5d0 in -[UITableViewRowData rectForFooterInSection:]
#5  0x30c3d430 in -[UITableViewRowData heightForTable]
#6  0x30c05430 in -[UITableView(_UITableViewPrivate) _updateContentSize]
#7  0x30c3ce0c in -[UITableView noteNumberOfRowsChanged]
#8  0x30c3c7c0 in -[UITableView reloadData]
#9  0x30e50e60 in -[UITableViewController viewWillAppear:]
#10 0x30c78810 in -[UINavigationController _startTransition:fromViewController:toViewController:]
#11 0x30c783b0 in -[UINavigationController _startDeferredTransitionIfNeeded]
#12 0x30c782a0 in -[UINavigationController viewWillLayoutSubviews]
#13 0x30c5c874 in -[UILayoutContainerView layoutSubviews]
#14 0x30c382d8 in -[UIView(CALayerDelegate) _layoutSublayersOfLayer:]
#15 0x33e871c0 in -[CALayer layoutSublayers]
#16 0x33e86edc in CALayerLayoutIfNeeded
#17 0x33e86844 in CA::Context::commit_transaction
#18 0x33e86474 in CA::Transaction::commit
#19 0x33e8e5dc in CA::Transaction::observer_callback
#20 0x32d5c830 in __CFRunLoopDoObservers
#21 0x32da4346 in CFRunLoopRunSpecific
#22 0x32da3c1e in CFRunLoopRunInMode
#23 0x31bb9374 in GSEventRunModal
#24 0x30bf3c30 in -[UIApplication _run]
#25 0x30bf2230 in UIApplicationMain
#26 0x00002260 in main at main.m:14

第三次呼叫:

#0  0x000086cc in -[RootViewController tableView:viewForHeaderInSection:] at RootViewController.m:444
#1  0x30c901a8 in -[UITableView(UITableViewInternal) _delegateWantsHeaderForSection:]
#2  0x30c3eebc in -[UITableView(_UITableViewPrivate) _updateVisibleHeadersAndFootersNow]
#3  0x30c3e578 in -[UITableView(_UITableViewPrivate) _updateVisibleCellsNow]
#4  0x30c3c514 in -[UITableView layoutSubviews]
#5  0x30c382d8 in -[UIView(CALayerDelegate) _layoutSublayersOfLayer:]
#6  0x33e871c0 in -[CALayer layoutSublayers]
#7  0x33e86edc in CALayerLayoutIfNeeded
#8  0x33e86844 in CA::Context::commit_transaction
#9  0x33e86474 in CA::Transaction::commit
#10 0x33e8e5dc in CA::Transaction::observer_callback
#11 0x32d5c830 in __CFRunLoopDoObservers
#12 0x32da4346 in CFRunLoopRunSpecific
#13 0x32da3c1e in CFRunLoopRunInMode
#14 0x31bb9374 in GSEventRunModal
#15 0x30bf3c30 in -[UIApplication _run]
#16 0x30bf2230 in UIApplicationMain
#17 0x00002260 in main at main.m:14

第四次呼叫:
#0  0x000086cc in -[RootViewController tableView:viewForHeaderInSection:] at RootViewController.m:444
#1  0x30c9712c in -[UITableView(UITableViewInternal) _sectionHeaderViewWithFrame:forSection:opaque:reuseViewIfPossible:]
#2  0x30c3f0b4 in -[UITableView(_UITableViewPrivate) _updateVisibleHeadersAndFootersNow]
#3  0x30c3e578 in -[UITableView(_UITableViewPrivate) _updateVisibleCellsNow]
#4  0x30c3c514 in -[UITableView layoutSubviews]
#5  0x30c382d8 in -[UIView(CALayerDelegate) _layoutSublayersOfLayer:]
#6  0x33e871c0 in -[CALayer layoutSublayers]
#7  0x33e86edc in CALayerLayoutIfNeeded
#8  0x33e86844 in CA::Context::commit_transaction
#9  0x33e86474 in CA::Transaction::commit
#10 0x33e8e5dc in CA::Transaction::observer_callback
#11 0x32d5c830 in __CFRunLoopDoObservers
#12 0x32da4346 in CFRunLoopRunSpecific
#13 0x32da3c1e in CFRunLoopRunInMode
#14 0x31bb9374 in GSEventRunModal
#15 0x30bf3c30 in -[UIApplication _run]
#16 0x30bf2230 in UIApplicationMain
#17 0x00002260 in main at main.m:14

第五次呼叫:
#0  0x000086cc in -[RootViewController tableView:viewForHeaderInSection:] at RootViewController.m:444
#1  0x30c901a8 in -[UITableView(UITableViewInternal) _delegateWantsHeaderForSection:]
#2  0x30c3eebc in -[UITableView(_UITableViewPrivate) _updateVisibleHeadersAndFootersNow]
#3  0x30c3e578 in -[UITableView(_UITableViewPrivate) _updateVisibleCellsNow]
#4  0x30c3c514 in -[UITableView layoutSubviews]
#5  0x30c382d8 in -[UIView(CALayerDelegate) _layoutSublayersOfLayer:]
#6  0x33e871c0 in -[CALayer layoutSublayers]
#7  0x33e86edc in CALayerLayoutIfNeeded
#8  0x33e86844 in CA::Context::commit_transaction
#9  0x33e86474 in CA::Transaction::commit
#10 0x33e8e5dc in CA::Transaction::observer_callback
#11 0x32d5c830 in __CFRunLoopDoObservers
#12 0x32da4346 in CFRunLoopRunSpecific
#13 0x32da3c1e in CFRunLoopRunInMode
#14 0x31bb9374 in GSEventRunModal
#15 0x30bf3c30 in -[UIApplication _run]
#16 0x30bf2230 in UIApplicationMain
#17 0x00002260 in main at main.m:14

第六次呼叫:
#0  0x000086cc in -[RootViewController tableView:viewForHeaderInSection:] at RootViewController.m:444
#1  0x30c9712c in -[UITableView(UITableViewInternal) _sectionHeaderViewWithFrame:forSection:opaque:reuseViewIfPossible:]
#2  0x30c3f0b4 in -[UITableView(_UITableViewPrivate) _updateVisibleHeadersAndFootersNow]
#3  0x30c3e578 in -[UITableView(_UITableViewPrivate) _updateVisibleCellsNow]
#4  0x30c3c514 in -[UITableView layoutSubviews]
#5  0x30c382d8 in -[UIView(CALayerDelegate) _layoutSublayersOfLayer:]
#6  0x33e871c0 in -[CALayer layoutSublayers]
#7  0x33e86edc in CALayerLayoutIfNeeded
#8  0x33e86844 in CA::Context::commit_transaction
#9  0x33e86474 in CA::Transaction::commit
#10 0x33e8e5dc in CA::Transaction::observer_callback
#11 0x32d5c830 in __CFRunLoopDoObservers
#12 0x32da4346 in CFRunLoopRunSpecific
#13 0x32da3c1e in CFRunLoopRunInMode
#14 0x31bb9374 in GSEventRunModal
#15 0x30bf3c30 in -[UIApplication _run]
#16 0x30bf2230 in UIApplicationMain
#17 0x00002260 in main at main.m:14

1
当你开始滚动表视图时,情况会变得更糟。同样的参数反复调用相同的函数。由于我的表视图从SQL数据库获取数据,我不得不为其中一些函数使用缓存。 - progrmr
2个回答

1
在我的代码中,numberOfSectionsInTableView 只会在响应 reloadData 调用时被调用一次。
你可以在 numberOfSectionsInTableView 中设置一个断点,并查看调用堆栈以了解它被如何和为什么被调用的。

根据我的经验,UITableView在不重复调用方面表现相当不错。检查重复调用的调用堆栈并查看发生了什么。这可能是你的错误 - 我以前确实见过这种情况发生过。 - Colin Barrett
即使是苹果的示例也会反复调用这些方法。在的SimpleSectionedTableView中的numberOfSectionsInTableView方法中添加一个NSLog行,你应该会看到多次调用(在这种情况下为2)...除非我的设置出了问题?! - Meltemi

0

它频繁调用它们的一个原因是,每次有[... reloadData]或其他操作时,事情可能会发生巨大变化,因此它不能假设事情与上次调用它们时一样。这就是为什么在文档中对缓存UITableViewCells进行了大量介绍的原因之一。

这里有一个演示这一点的类:

@interface WinnersViewController : UITableViewController {
}
- (void)setDataSourceAndDelegate;
@end

在我的viewDidLoad中,我调用了一个名为setDataSourceAndDelegate的方法,该方法将三个类之一设置为表视图上的数据源和委托属性。

- (void)viewDidLoad {
    [self setDataSourceAndDelegate];
    [super viewDidLoad];
}

我有三个不同的对象作为数据源和委托(组合),我使用一个抽象类来管理它们:

WinnersViewDefaultDSD
WinnersViewHasLocationDSD
WinnersViewHasExampleDSD

(出于明显的原因,我已经用“Example”替换了实际的类名。)

现在这些类向表视图返回非常不同的东西,有时当我的应用程序状态改变时(比如调用CoreLocation或用户选择一个类示例的实例),setDataSourceAndDelegate方法会再次被调用,并且它可以在运行时更改所在的对象。

因此,UITableView调用其委托和数据源的许多方法是因为它需要支持开发人员对表视图进行这种操作。如果某些方法只被调用一次,然后假定答案不会改变,那么它就不会那么灵活。

无论如何,希望这是一个有用的例子。

(哦,如果有人要模仿这个,请注意TableView上的委托和数据源属性是复制而不是保留,因此您必须对DSD类的实例进行额外的保留,否则您将会遇到一个神秘但频繁的崩溃。)

(另外,我需要覆盖各种DSD子类的viewDidAppear,并最终需要执行以下操作:)

- (void)viewDidAppear:(BOOL)animated
{
    [[(UITableView*)self.view delegate] delegatedViewDidAppear];
}

(这是一个小技巧,让我不必在类中跟踪DSD对象。)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接