8wDlpd.png
8wDFp9.png
8wDEOx.png
8wDMfH.png
8wDKte.png

TableView 冻结

Wheatley 1月前

22 0

我有一个项目数组和用于显示它们的 TableView。项目包含 4 个属性。并且有一个方法可以随机生成项目。最初,数组为空,但在 viewDidLoad 中我有方法,

我有一组项目和用于显示它们的 TableView。项目包含 4 个属性。并且有一个方法可以随机生成项目。最初,数组为空,但在 viewDidLoad 中,我有一个方法,它将 100 个项目附加到数组中,延迟约 1 秒。数组会一直附加,直到其计数达到约 1_000_000 个项目。当我启动应用程序时,它会冻结。

这是我的 tableView 方法:

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return model.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: DealCell.reuseIidentifier, for: indexPath) as! DealCell
        guard model.count != 0 else {
            cell.instrumentNameLabel.text = "no data"
            cell.priceLabel.text = "no data"
            cell.amountLabel.text = "no data"
            cell.sideLabel.text = "no data"
            return cell
        }
        cell.instrumentNameLabel.text = "\(model[indexPath.row].instrumentName)"
        cell.priceLabel.text = "\(model[indexPath.row].price)"
        cell.amountLabel.text = "\(model[indexPath.row].amount)"
        cell.sideLabel.text = "\(model[indexPath.row].side)"
        return cell
    }

附加数组的函数:

server.subscribeToUpdate { updates in
            self.model.append(contentsOf: updates)
            self.tableView.reloadData()
        }

如何解决这个问题?可能是将 \'numberOfRowsInSection\' 的计数器设置为 100,然后当滚动到第 100 个项目时将其增加到 200 等。或者有更简洁的解决方案吗?

尝试使用 ReusableCell,但没有任何反应。

帖子版权声明 1、本帖标题:TableView 冻结
    本站网址:http://xjnalaquan.com/
2、本网站的资源部分来源于网络,如有侵权,请联系站长进行删除处理。
3、会员发帖仅代表会员个人观点,并不代表本站赞同其观点和对其真实性负责。
4、本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
5、站长邮箱:yeweds@126.com 除非注明,本帖由Wheatley在本站《uitableview》版块原创发布, 转载请注明出处!
最新回复 (0)
  • 该生成是否发生在主线程之外?听起来您正在阻止主线程。尝试将对象生成分派到另一个队列,然后在主队列上重新加载表视图。

  • @Paulw11 是的,我使用了自定义队列: letqueue = DispatchQueue(label: \'CustomQueue\')queue.async { 生成项目 }。但根据 CPU 记录,CPU 和线程 1 使用了 100% 的资源。

  • 我不知道您在这里真正想做什么......您的代码看起来就像是在做一个“压力测试”或类似的事情。

    但是,为了帮助您理解为什么您的应用会“冻结”——

    您的 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 行。

    但是,您会发现可以 生成记录

    它看起来是这样的:

    enter image description here enter image description here

    enter image description here enter image description here

    交易结构

    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)
            }
        }
    }
    

    注意: 仅仅是示例代码! 它不打算、也不应被视为“可用于生产”。

  • 非常感谢你提出逐步更新表格的想法!是的,事实上这有点像压力测试。@DonMag

  • 在 collectionview 的情况下如何处理类似的情况,我们有来自 BLE 设备 20 数据集/秒的数据,并且我们尝试每秒更新一次标签,并且在我们开始屏幕录制的情况下 2 小时后应用程序开始冻结。

  • @NarinderSingh - 抱歉,不知道...您需要进行一些调试和/或分析才能找出发生了什么。内存过载?尝试更新数千个值?

  • @DonMag 是的,我的集合中有 7-8 个静态单元,我需要在从 BLE 设备获取值后立即更新这些值。我尝试过分析,内存看起来不错,即使运行 2 小时后内存仍然在 120-130 MB 左右。看起来,操作在 UI 线程上排队,由于频繁重新加载而变得很大,减少每秒的 realod 可能是唯一的解决方案。

  • 下面的代码应该通过使用 DispatchQueue 解除主线程的阻塞来解决您的问题

    server.subscribeToDeals { deals in
           self.model.append(contentsOf: deals)
           DispatchQueue.main.async {
              self.tableView.reloadData()
           } 
        }
    
返回
作者最近主题: