Xcode 10.3 (10G8) 使用编译模式破坏应用程序 = "Whole Module"
Xcode 10.3 (10G8) breaks app with compilation mode = "Whole Module"
我将我的 iOS 应用程序的 7.6 版推送到 App Store 并注意到该应用程序突然包含许多在调试期间未显示的错误(奇怪的行为)。
由于该应用程序在 xCode 9.x 中运行良好,我强烈怀疑问题是从 Xcode 10.3 开始出现的。
我正在使用 Swift 4.2(Swift 5 的转换是为了下次更新)
经过几个小时的调查,我找到了问题所在:当编译模式 = "whole module
" 时错误出现,当设置为 "incremental
" 时错误消失。在调试模式下(当应用程序 运行 出 Xcode 时)编译模式设置为“incremental
”以发布其“whole module
”(这是标准配置你在 Xcode 10.x 中创建了一个新项目我怀疑)这解释了为什么我们在调试测试期间没有看到问题。
另请注意,更改为旧版构建系统并未解决问题。只有设置 Compilation mode = "incremental
" 解决了问题。
分析:
- 我追踪到这个问题是因为我的所有 TableView 都没有调用委托。
我有以下简单的层次结构:
- ViewTableRoot 的代码:
class ViewTableRoot : UITableView, UITableViewDelegate, UITableViewDataSource {
var didScrollToOffset : ( (CGFloat) -> Void )?
var didEndScrolling : ( (CGFloat) -> Void )?
var didChangeEditing : ( ( ) -> Void )?
//MARK: lifecycle
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
override init(frame: CGRect, style: UITableView.Style) {
super.init(frame: frame, style: style)
setup();
}
func setup() {
//set the corner radius of the layer so that the sliding of the cells underneath the rounded headers does not show up
layer.cornerRadius = 5
//setup myself as delegate and data source
delegate = self
dataSource = self
}
deinit {
let className = String(describing: self)
log.debug("**********\(className)")
}
//MARK: - public API
override func setEditing(_ editing: Bool, animated: Bool) {
super.setEditing(editing, animated: animated)
didChangeEditing?()
}
//MARK: - scrollview delegate
func scrollViewDidScroll(_ scrollView: UIScrollView) {
//because we are also getting events when swiping on the cells, we need to see the difference between
//swipig on the cell and swiping in the "actual" table => we do this by checking the frame size
guard scrollView.frame == frame else { return }
didScrollToOffset?(scrollView.contentOffset.y)
}
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
//because we are also getting events when swiping on the cells, we need to see the difference between
//swipig on the cell and swiping in the "actual" table => we do this by checking the frame size
guard scrollView.frame == frame else { return }
if !decelerate {
didEndScrolling?(scrollView.contentOffset.y)
}
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
//because we are also getting events when swiping on the cells, we need to see the difference between
//swipig on the cell and swiping in the "actual" table => we do this by checking the frame size
guard scrollView.frame == frame else { return }
didEndScrolling?(contentOffset.y)
}
//MARK: - UITableViewDataSource
func numberOfSections(in tableView: UITableView) -> Int {
preconditionFailure("Must be implemented by derrived class")
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
preconditionFailure("Must be implemented by derrived class")
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
preconditionFailure("Must be implemented by derrived class")
}
//MARK: - UITableViewDelegate
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
}
- ViewTableSettings 代码:
class ViewTableSettings : ViewTableRoot {
var settings : [[Setting]]? {
didSet {
reloadData()
}
}
var didPressSetting : ((Setting, CGRect) -> (Void))?
//MARK: lifecycle
override func setup() {
super.setup()
log.debug("delegate : \(delegate)")
//register xibs
register(CellTableSetting.nib, forCellReuseIdentifier: CellTableSetting.reuseIdentifier)
}
//MARK: - UITableView
override func numberOfSections(in tableView: UITableView) -> Int {
let count = settings?.count ?? 0
log.debug("count: \(count)")
log.debug("delegate : \(delegate)")
return count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let count = settings?[section].count ?? 0
log.debug("count: \(count)")
log.debug("delegate : \(delegate)")
return count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
log.debug("delegate : \(delegate)")
//ask for a new cell
let cell = tableView.dequeueReusableCell(withIdentifier: CellTableSetting.reuseIdentifier, for: indexPath) as! CellTableSetting
guard let setting = settings?[indexPath.section][indexPath.row] else {
preconditionFailure("Asking CellTableSetting but no Setting model defined")
}
//load up!
cell.setting = setting
cell.lastCell = indexPath.section != numberOfSections - 1 ? false : indexPath.row == (numberOfRows(inSection:indexPath.section) - 1)
//return cell to use
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
log.debug("-")
return CellTableSetting.height
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
log.debug("-")
tableView.deselectRow(at:indexPath, animated: true)
guard let setting = settings?[indexPath.section][indexPath.row] else {
return
}
didPressSetting?(setting,rectForRow(at: indexPath))
}
func tableView(_: UITableView, viewForFooterInSection: Int) -> UIView? {
log.debug("-")
guard viewForFooterInSection < (numberOfSections-1) else {
let version = UILabel()
version.text = UIApplication.AppVersionAndBuildPrettyPrint
version.font = UIFont.defaultBoldFont(size: 12)
version.textColor = PaintCode.mainLightGray_a50
version.textAlignment = .center
return version
}
let v = UIView()
v.backgroundColor = PaintCode.mainLightGray_a50
return v
}
func tableView(_: UITableView, heightForFooterInSection: Int) -> CGFloat {
log.debug("-")
return heightForFooterInSection < (numberOfSections-1) ? 5 : 40
}
}
- 如您所见,
ViewTableRoot
声明遵守 UITableViewDelegate
(也遵守 UITableViewDataSource
,但现在除此之外)
- 委托实际上分配给了
ViewTableRoot
中的 self
,但实际的委托函数是在派生的 ViewTableSettings
中实现的(同样这在 Xcode 中非常有效9.x)
- 当编译模式 =“
Whole Module
”时,这些委托函数不会被调用 => 这就是错误
- 当设置为“
incremental
”时,这些委托函数被调用得很好!
为了更深入地了解问题,我进行了其他测试:
- 切换到 "legacy build system"(通过 Xcode/file/project 设置)并不能解决问题;只要
Whole Module
启用,问题仍然存在
- 当我在 ViewTableRoot 中创建空委托函数并在 ViewTableSettings 中覆盖它们时,它确实有效:-o
- 我确实在
ViewTableSettings
中验证委托确实设置为 ViewTableSettings
的实例而不是 ViewTableRoot(在这种情况下不会实现任何委托函数)
我的想法
- 我觉得我偶然发现了(新的?)构建系统中的错误?
- 还有其他人 运行 遇到类似问题吗?
是的,我和你有同样的问题。当编译模式为 "Whole Module" 时,Collectionview 控制器被弄乱了,但 Tableview 控制器没有。 result on simulator device。也许你可以尝试直接使用 UITableViewController 而不是遵循 TableView 协议。
安装了最新的Xcode 11.4 (11E146) 以及该版本中待解决的问题接缝。我重新启用了整个模块优化,一切都按预期工作。所以...原来是XCdoe的bug!
我将我的 iOS 应用程序的 7.6 版推送到 App Store 并注意到该应用程序突然包含许多在调试期间未显示的错误(奇怪的行为)。
由于该应用程序在 xCode 9.x 中运行良好,我强烈怀疑问题是从 Xcode 10.3 开始出现的。
我正在使用 Swift 4.2(Swift 5 的转换是为了下次更新)
经过几个小时的调查,我找到了问题所在:当编译模式 = "whole module
" 时错误出现,当设置为 "incremental
" 时错误消失。在调试模式下(当应用程序 运行 出 Xcode 时)编译模式设置为“incremental
”以发布其“whole module
”(这是标准配置你在 Xcode 10.x 中创建了一个新项目我怀疑)这解释了为什么我们在调试测试期间没有看到问题。
另请注意,更改为旧版构建系统并未解决问题。只有设置 Compilation mode = "incremental
" 解决了问题。
分析:
- 我追踪到这个问题是因为我的所有 TableView 都没有调用委托。
我有以下简单的层次结构:
- ViewTableRoot 的代码:
class ViewTableRoot : UITableView, UITableViewDelegate, UITableViewDataSource {
var didScrollToOffset : ( (CGFloat) -> Void )?
var didEndScrolling : ( (CGFloat) -> Void )?
var didChangeEditing : ( ( ) -> Void )?
//MARK: lifecycle
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
override init(frame: CGRect, style: UITableView.Style) {
super.init(frame: frame, style: style)
setup();
}
func setup() {
//set the corner radius of the layer so that the sliding of the cells underneath the rounded headers does not show up
layer.cornerRadius = 5
//setup myself as delegate and data source
delegate = self
dataSource = self
}
deinit {
let className = String(describing: self)
log.debug("**********\(className)")
}
//MARK: - public API
override func setEditing(_ editing: Bool, animated: Bool) {
super.setEditing(editing, animated: animated)
didChangeEditing?()
}
//MARK: - scrollview delegate
func scrollViewDidScroll(_ scrollView: UIScrollView) {
//because we are also getting events when swiping on the cells, we need to see the difference between
//swipig on the cell and swiping in the "actual" table => we do this by checking the frame size
guard scrollView.frame == frame else { return }
didScrollToOffset?(scrollView.contentOffset.y)
}
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
//because we are also getting events when swiping on the cells, we need to see the difference between
//swipig on the cell and swiping in the "actual" table => we do this by checking the frame size
guard scrollView.frame == frame else { return }
if !decelerate {
didEndScrolling?(scrollView.contentOffset.y)
}
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
//because we are also getting events when swiping on the cells, we need to see the difference between
//swipig on the cell and swiping in the "actual" table => we do this by checking the frame size
guard scrollView.frame == frame else { return }
didEndScrolling?(contentOffset.y)
}
//MARK: - UITableViewDataSource
func numberOfSections(in tableView: UITableView) -> Int {
preconditionFailure("Must be implemented by derrived class")
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
preconditionFailure("Must be implemented by derrived class")
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
preconditionFailure("Must be implemented by derrived class")
}
//MARK: - UITableViewDelegate
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
}
- ViewTableSettings 代码:
class ViewTableSettings : ViewTableRoot {
var settings : [[Setting]]? {
didSet {
reloadData()
}
}
var didPressSetting : ((Setting, CGRect) -> (Void))?
//MARK: lifecycle
override func setup() {
super.setup()
log.debug("delegate : \(delegate)")
//register xibs
register(CellTableSetting.nib, forCellReuseIdentifier: CellTableSetting.reuseIdentifier)
}
//MARK: - UITableView
override func numberOfSections(in tableView: UITableView) -> Int {
let count = settings?.count ?? 0
log.debug("count: \(count)")
log.debug("delegate : \(delegate)")
return count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let count = settings?[section].count ?? 0
log.debug("count: \(count)")
log.debug("delegate : \(delegate)")
return count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
log.debug("delegate : \(delegate)")
//ask for a new cell
let cell = tableView.dequeueReusableCell(withIdentifier: CellTableSetting.reuseIdentifier, for: indexPath) as! CellTableSetting
guard let setting = settings?[indexPath.section][indexPath.row] else {
preconditionFailure("Asking CellTableSetting but no Setting model defined")
}
//load up!
cell.setting = setting
cell.lastCell = indexPath.section != numberOfSections - 1 ? false : indexPath.row == (numberOfRows(inSection:indexPath.section) - 1)
//return cell to use
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
log.debug("-")
return CellTableSetting.height
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
log.debug("-")
tableView.deselectRow(at:indexPath, animated: true)
guard let setting = settings?[indexPath.section][indexPath.row] else {
return
}
didPressSetting?(setting,rectForRow(at: indexPath))
}
func tableView(_: UITableView, viewForFooterInSection: Int) -> UIView? {
log.debug("-")
guard viewForFooterInSection < (numberOfSections-1) else {
let version = UILabel()
version.text = UIApplication.AppVersionAndBuildPrettyPrint
version.font = UIFont.defaultBoldFont(size: 12)
version.textColor = PaintCode.mainLightGray_a50
version.textAlignment = .center
return version
}
let v = UIView()
v.backgroundColor = PaintCode.mainLightGray_a50
return v
}
func tableView(_: UITableView, heightForFooterInSection: Int) -> CGFloat {
log.debug("-")
return heightForFooterInSection < (numberOfSections-1) ? 5 : 40
}
}
- 如您所见,
ViewTableRoot
声明遵守UITableViewDelegate
(也遵守UITableViewDataSource
,但现在除此之外) - 委托实际上分配给了
ViewTableRoot
中的self
,但实际的委托函数是在派生的ViewTableSettings
中实现的(同样这在 Xcode 中非常有效9.x) - 当编译模式 =“
Whole Module
”时,这些委托函数不会被调用 => 这就是错误 - 当设置为“
incremental
”时,这些委托函数被调用得很好!
为了更深入地了解问题,我进行了其他测试:
- 切换到 "legacy build system"(通过 Xcode/file/project 设置)并不能解决问题;只要
Whole Module
启用,问题仍然存在 - 当我在 ViewTableRoot 中创建空委托函数并在 ViewTableSettings 中覆盖它们时,它确实有效:-o
- 我确实在
ViewTableSettings
中验证委托确实设置为ViewTableSettings
的实例而不是 ViewTableRoot(在这种情况下不会实现任何委托函数)
我的想法
- 我觉得我偶然发现了(新的?)构建系统中的错误?
- 还有其他人 运行 遇到类似问题吗?
是的,我和你有同样的问题。当编译模式为 "Whole Module" 时,Collectionview 控制器被弄乱了,但 Tableview 控制器没有。 result on simulator device。也许你可以尝试直接使用 UITableViewController 而不是遵循 TableView 协议。
安装了最新的Xcode 11.4 (11E146) 以及该版本中待解决的问题接缝。我重新启用了整个模块优化,一切都按预期工作。所以...原来是XCdoe的bug!