我不知道您在这里真正想做什么......您的代码看起来就像是在做一个“压力测试”或类似的事情。
但是,为了帮助您理解为什么您的应用会“冻结”——
您的 for i in 0...dealsCount {
循环将运行得 非常 快。每 100 次迭代可能需要 1 或 2 千分之一 秒。如果您 .reloadData()
每 100 次循环调用一次,您的代码就会尝试不断更新 UI。这意味着您的 UI 将显示为“冻结”。
这是一个您可能会觉得有用的例子...
首先,我们将添加一个“状态标签”和一个进度视图,以显示生成交易时的进度。我们将每创建 1000 个新交易更新一次这些内容。
其次,当我们生成新的交易时,我们会将它们直接附加到控制器的 var model: [Deal] = []
数组中(而不是构建新的数组并定期附加它们)。
第三,我们只会调用 .reloadData()
:
-
前 1,000 笔交易
-
然后每 100,000 笔交易
-
最后当我们生成了所有 100 多万个
正如我所说的,我不知道你真正在做什么......但是在我们添加接下来的 100,000 条记录之前,不太可能有人会滚动浏览前 1,000 行。
但是,您会发现可以 生成记录 时
它看起来是这样的:
交易结构
struct Deal {
var id: Int64 = 0
var dateModifier: Date = Date()
var instrumentName: String = ""
var price: Double = 0
var amount: Double = 0
}
简单的多行标签单元格类
class DealCell: UITableViewCell {
let theLabel = UILabel()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() {
theLabel.numberOfLines = 0
theLabel.backgroundColor = UIColor(white: 0.95, alpha: 1.0)
theLabel.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(theLabel)
let g = contentView.layoutMarginsGuide
NSLayoutConstraint.activate([
theLabel.topAnchor.constraint(equalTo: g.topAnchor),
theLabel.leadingAnchor.constraint(equalTo: g.leadingAnchor),
theLabel.trailingAnchor.constraint(equalTo: g.trailingAnchor),
theLabel.bottomAnchor.constraint(equalTo: g.bottomAnchor),
])
}
func fillData(_ aDeal: Deal) {
let i = aDeal.id
let d = aDeal.dateModifier
let nm = aDeal.instrumentName
let p = String(format: "%0.2f", aDeal.price)
let a = String(format: "%0.2f", aDeal.amount)
theLabel.text = "\(i): \(nm)\nPrice: \(p) / Amount: \(a)\n\(d)"
}
}
演示视图控制器
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var model: [Deal] = []
let instrumentNames: [String] = [
"accordion",
"acoustic guitar",
"bagpipes",
"banjo",
"bass guitar",
"bongo drums",
"bugle",
"cello",
"clarinet",
"cymbals",
"drums",
"electric guitar",
"flute",
"French horn",
"harmonica",
"harp",
"keyboard",
"maracas",
"organ",
"pan flute (pan pipes)",
"piano",
"recorder",
"saxophone",
"sitar",
"tambourine",
"triangle",
"trombone",
"trumpet",
"tuba",
"ukulele",
"violin",
"xylophone",
]
let tableView = UITableView()
let progressView = UIProgressView()
let statusLabel = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
[statusLabel, progressView, tableView].forEach { v in
v.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(v)
}
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
statusLabel.topAnchor.constraint(equalTo: g.topAnchor, constant: 8.0),
statusLabel.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 8.0),
statusLabel.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -8.0),
progressView.topAnchor.constraint(equalTo: statusLabel.bottomAnchor, constant: 8.0),
progressView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
progressView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
tableView.topAnchor.constraint(equalTo: progressView.bottomAnchor, constant: 8.0),
tableView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
tableView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
tableView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0.0),
])
tableView.register(DealCell.self, forCellReuseIdentifier: "c")
tableView.dataSource = self
tableView.delegate = self
statusLabel.textAlignment = .center
statusLabel.textColor = .systemRed
subscribeToDeals()
}
func updateProgress(currentCount: Int64, futureCount: Int64) {
// update the status Lable and progress bar
statusLabel.text = "Generated \(currentCount) of \(futureCount)"
progressView.progress = Float(currentCount) / Float(futureCount)
// only reload the table if we're at
// the first 1_000, or
// every 100_000, or
// we're finished generating Deals
// if it's the first update
if currentCount == 1_000 {
tableView.reloadData()
}
// else if we're at an even 100_000
else if currentCount % 100_000 == 0 {
tableView.reloadData()
}
// else if we've generated all
else if currentCount == futureCount {
tableView.reloadData()
}
}
func subscribeToDeals() {
let bkgQueue = DispatchQueue(label: "subscribing", qos: .background)
bkgQueue.async{
let dealsCount = Int64.random(in: 1_000_000..<1_001_000)
for i in 0...dealsCount {
let currentTimeStamp = Date().timeIntervalSince1970
let timeStampRandomizer = Double.random(in: 50_000...50_000_000)
let deal = Deal (
id: i,
dateModifier: Date(timeIntervalSince1970: Double.random(in: currentTimeStamp - timeStampRandomizer...currentTimeStamp)),
instrumentName: self.instrumentNames.shuffled().first!,
price: Double.random(in: 60...70),
amount: Double.random(in: 1_000_000...50_000_000)
)
// append directly to data
self.model.append(deal)
// if we're at a 1_000 point
if i % 1_000 == 0 {
DispatchQueue.main.async {
self.updateProgress(currentCount: i, futureCount: dealsCount)
}
}
}
// we've generated all deals
DispatchQueue.main.async {
self.updateProgress(currentCount: dealsCount, futureCount: dealsCount)
}
print("Done generating \(dealsCount) Deals!")
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return model.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let c = tableView.dequeueReusableCell(withIdentifier: "c", for: indexPath) as! DealCell
c.fillData(model[indexPath.row])
return c
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if model.count > 1_000_000 {
tableView.scrollToRow(at: IndexPath(row: 900_000, section: 0), at: .middle, animated: false)
}
}
}
注意: 这 仅仅是示例代码! 它不打算、也不应被视为“可用于生产”。