当代码在 loadView 中时:无法执行支持代码以读取进程中的 Objective-C class 数据
When code is in loadView: Could not execute support code to read Objective-C class data in the process
假设我以编程方式在导航控制器中创建了一个 tableView。删除故事板文件及其参考后。我将所有 UI 初始化和约束代码放入 loadView()
中,如下代码所示。
运行真机,但是table视图没有出现,很快就弹出这个警告
如果我将这些代码放入 viewDidLoad
,一切正常。那么,我该如何追踪这个问题呢?我已经搜索了一些类似的线程,但没有解决我的问题。
warning: could not execute support code to read Objective-C class data
in the process. This may reduce the quality of type information
available.
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
let table = UITableView()
var pictures = [Picture]()
let defaults = UserDefaults.standard
override func viewDidLoad() {
super.viewDidLoad()
// if put code in loadView() into here, everything works fine.
loadData()
}
override func loadView() {
title = "My Pictures"
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(addPicture))
view.addSubview(table)
table.delegate = self
table.dataSource = self
table.register(PictureCell.self, forCellReuseIdentifier: "picture")
table.translatesAutoresizingMaskIntoConstraints = false
table.rowHeight = 120
NSLayoutConstraint.activate([
table.topAnchor.constraint(equalTo: view.topAnchor),
table.leadingAnchor.constraint(equalTo: view.leadingAnchor),
table.trailingAnchor.constraint(equalTo: view.trailingAnchor),
table.bottomAnchor.constraint(equalTo: view.bottomAnchor),
])
}
func loadData() {
DispatchQueue.global().async { [weak self] in
if let savedPcitures = self?.defaults.object(forKey: "pictures") as? Data {
let jsonDecoder = JSONDecoder()
do {
self?.pictures = try jsonDecoder.decode([Picture].self, from: savedPcitures)
} catch {
print("Failed to load pictures.")
}
}
DispatchQueue.main.async {
self?.table.reloadData()
}
}
}
@objc func addPicture() {
let picker = UIImagePickerController()
if UIImagePickerController.isSourceTypeAvailable(.camera) {
picker.sourceType = .camera
} else {
fatalError("Camera is not available, please use real device")
}
picker.allowsEditing = true
picker.delegate = self
present(picker, animated: true)
}
...
如果您覆盖 loadView()
,则由您来创建和设置控制器“根”视图。
如果将此添加到代码中:
override func loadView() {
title = "My Pictures"
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(addPicture))
// add these three lines
let newView = UIView()
newView.backgroundColor = .white
view = newView
view.addSubview(table)
// ... rest of your original code
您应该能够导航到您的控制器。
就是说 --- 除非您有 特定原因 来覆盖 loadView()
,否则您可能不应该这样做。您示例中的所有代码通常会进入 viewDidLoad()
.
编辑 - 一些说明...
我觉得 Apple 的文档有点含糊...
据我了解,任何仅通过代码创建的 UIViewController
后代的视图控制器(UIViewController
、UITableViewController
、UICollectionViewController
等)都会生成其拥有“根”视图 除非我们重写 loadView()
在这种情况下,由我们来实例化并提供该根视图。
那么,我们为什么要提供这种观点?我们可能想提供一个 custom UIView
subclass。例如,如果我有一个“渐变”视图,将其基础层 class 设置为 CAGradientLayer
- 这样我就不必添加和管理子层。我可以覆盖 loadView()
并将我的控制器的根视图设置为该渐变视图。
我在 GitHub 上放了一个完整的例子。项目中的 仅 故事板是必需的 LaunchScreen
--- 其他一切仅通过代码完成。
它以导航控制器中的 3 个按钮开始:
- 第一个按钮推送到带有标签
的简单UIViewController
- 第二个按钮推送到
UITableViewController
,其中选择行推送到“详细信息”UIViewController
- 第三个按钮推送到带有标签和渐变背景的简单
UIViewController
第三个示例是唯一一个我覆盖 loadView()
以使用自定义渐变视图的示例。所有其他人处理 viewDidLoad()
中的所有设置
这是项目的 link:https://github.com/DonMag/CodeOnly
正如 DonMag 上面所说,我需要在这里给 view
一个值,因为默认情况下它是 nil(不使用界面构建器)。
我发现这里有一个参考可以解释何时使用 viewDidLoad
或 loadView
,在我的例子中,我使用第二种方法以编程方式创建所有视图。
参考:
When you load your view from a NIB and want to perform further
customization after launch, use viewDidLoad.
If you want to create your view programtically (not using Interface
Builder), use loadView.
更新:
根据 DonMag 的示例。很明显,我们可以使用两种方式来设置视图,在 viewDidLoad
或 loadView
中。使用 loadView
的最佳实践是当我们需要使用一些自定义 UIView
作为根视图时。与此同时,我找到了一篇关于这个主题的好文章。这是一个旧的,虽然我认为概念不会改变。
http://roopc.net/posts/2015/loadview-vs-viewdidload/
To conclude, we can set up the view hierarchy in either loadView or
viewDidLoad, as long as we’re aware of when which method gets called.
The most important thing to remember is that if you override loadView,
you should set the view property, while if you override viewDidLoad,
you should only read the view property, which should have been already
set.
假设我以编程方式在导航控制器中创建了一个 tableView。删除故事板文件及其参考后。我将所有 UI 初始化和约束代码放入 loadView()
中,如下代码所示。
运行真机,但是table视图没有出现,很快就弹出这个警告
如果我将这些代码放入 viewDidLoad
,一切正常。那么,我该如何追踪这个问题呢?我已经搜索了一些类似的线程,但没有解决我的问题。
warning: could not execute support code to read Objective-C class data in the process. This may reduce the quality of type information available.
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
let table = UITableView()
var pictures = [Picture]()
let defaults = UserDefaults.standard
override func viewDidLoad() {
super.viewDidLoad()
// if put code in loadView() into here, everything works fine.
loadData()
}
override func loadView() {
title = "My Pictures"
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(addPicture))
view.addSubview(table)
table.delegate = self
table.dataSource = self
table.register(PictureCell.self, forCellReuseIdentifier: "picture")
table.translatesAutoresizingMaskIntoConstraints = false
table.rowHeight = 120
NSLayoutConstraint.activate([
table.topAnchor.constraint(equalTo: view.topAnchor),
table.leadingAnchor.constraint(equalTo: view.leadingAnchor),
table.trailingAnchor.constraint(equalTo: view.trailingAnchor),
table.bottomAnchor.constraint(equalTo: view.bottomAnchor),
])
}
func loadData() {
DispatchQueue.global().async { [weak self] in
if let savedPcitures = self?.defaults.object(forKey: "pictures") as? Data {
let jsonDecoder = JSONDecoder()
do {
self?.pictures = try jsonDecoder.decode([Picture].self, from: savedPcitures)
} catch {
print("Failed to load pictures.")
}
}
DispatchQueue.main.async {
self?.table.reloadData()
}
}
}
@objc func addPicture() {
let picker = UIImagePickerController()
if UIImagePickerController.isSourceTypeAvailable(.camera) {
picker.sourceType = .camera
} else {
fatalError("Camera is not available, please use real device")
}
picker.allowsEditing = true
picker.delegate = self
present(picker, animated: true)
}
...
如果您覆盖 loadView()
,则由您来创建和设置控制器“根”视图。
如果将此添加到代码中:
override func loadView() {
title = "My Pictures"
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(addPicture))
// add these three lines
let newView = UIView()
newView.backgroundColor = .white
view = newView
view.addSubview(table)
// ... rest of your original code
您应该能够导航到您的控制器。
就是说 --- 除非您有 特定原因 来覆盖 loadView()
,否则您可能不应该这样做。您示例中的所有代码通常会进入 viewDidLoad()
.
编辑 - 一些说明...
我觉得 Apple 的文档有点含糊...
据我了解,任何仅通过代码创建的 UIViewController
后代的视图控制器(UIViewController
、UITableViewController
、UICollectionViewController
等)都会生成其拥有“根”视图 除非我们重写 loadView()
在这种情况下,由我们来实例化并提供该根视图。
那么,我们为什么要提供这种观点?我们可能想提供一个 custom UIView
subclass。例如,如果我有一个“渐变”视图,将其基础层 class 设置为 CAGradientLayer
- 这样我就不必添加和管理子层。我可以覆盖 loadView()
并将我的控制器的根视图设置为该渐变视图。
我在 GitHub 上放了一个完整的例子。项目中的 仅 故事板是必需的 LaunchScreen
--- 其他一切仅通过代码完成。
它以导航控制器中的 3 个按钮开始:
- 第一个按钮推送到带有标签 的简单
- 第二个按钮推送到
UITableViewController
,其中选择行推送到“详细信息”UIViewController
- 第三个按钮推送到带有标签和渐变背景的简单
UIViewController
UIViewController
第三个示例是唯一一个我覆盖 loadView()
以使用自定义渐变视图的示例。所有其他人处理 viewDidLoad()
这是项目的 link:https://github.com/DonMag/CodeOnly
正如 DonMag 上面所说,我需要在这里给 view
一个值,因为默认情况下它是 nil(不使用界面构建器)。
我发现这里有一个参考可以解释何时使用 viewDidLoad
或 loadView
,在我的例子中,我使用第二种方法以编程方式创建所有视图。
参考:
When you load your view from a NIB and want to perform further customization after launch, use viewDidLoad.
If you want to create your view programtically (not using Interface Builder), use loadView.
更新:
根据 DonMag 的示例。很明显,我们可以使用两种方式来设置视图,在 viewDidLoad
或 loadView
中。使用 loadView
的最佳实践是当我们需要使用一些自定义 UIView
作为根视图时。与此同时,我找到了一篇关于这个主题的好文章。这是一个旧的,虽然我认为概念不会改变。
http://roopc.net/posts/2015/loadview-vs-viewdidload/
To conclude, we can set up the view hierarchy in either loadView or viewDidLoad, as long as we’re aware of when which method gets called. The most important thing to remember is that if you override loadView, you should set the view property, while if you override viewDidLoad, you should only read the view property, which should have been already set.