问题描述
表格视图中有5个单元格,每个单元格都有倒计时,但倒计时运行很快,一次调用减少了1秒以上。
这是我的表格视图单元格类,我在这里创建了一个labelSatus
class ActivityCell: UITableViewCell {
@IBOutlet weak var labelStatus: UILabel!
//Variable Declaration
var timer: Timer?
var totalTime:Double = 0
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(_ selected: Bool,animated: Bool) {
super.setSelected(selected,animated: animated)
}
func designCell(data:ActivityModal,removeTimer:Bool){
if removeTimer {
if let timer = self.timer {
timer.invalidate()
self.timer = nil
}
}
else{
let timeStampLimit = (data.date)/1000 + 86400 //for 24 hours
let currentTimeStamp = NSDate().timeIntervalSince1970
if timeStampLimit > currentTimeStamp{
self.totalTime = timeStampLimit - currentTimeStamp
self.timer = Timer.scheduledTimer(timeInterval: 1.0,target: self,selector: #selector(updateTimer),userInfo: nil,repeats: true)
}
}
}
@objc func updateTimer() {
self.labelStatus.text = String.getString(CommonUtils.convertTimeStampToHour(unixtimeInterval: self.totalTime,dateFormat:"HH:mm:ss"))
if self.totalTime != 0 {
self.totalTime -= 1 // decrease counter timer
} else {
if let timer = self.timer {
timer.invalidate()
self.timer = nil
}
}
}
}
解决方法
在表格视图单元格上使用计时器时需要注意一些问题。请注意,UITableView
重用了单元格对象,因此在滚动后单元格从屏幕上消失后,它将被重用以显示在表格视图中的另一个位置。
假设您在 designCell()
方法中调用 tableView:(_:cellForRowAt:)
,您可能会为单个单元格创建更多计时器(即,许多计时器将触发同一单元格的 updateTimer()
方法)。请注意,如果计时器仍然有效,则在删除对它的所有引用后,将不会释放 Timer
。此时,您可能正在为特定单元格创建一个新计时器,而没有机会使旧计时器失效。
这将通过在表视图单元子类的 preapreForReuse()
方法中停止计时器来处理。当单元格将被重用时调用此方法:
class ActivityCell: UITableViewCell {
override func prepareForReuse() {
super.prepareForReuse()
self.timer?.invalidate()
self.timer = nil
}
}
这将解决(一个)计时器问题,但还有一个问题:您将丢失该单元格跟踪的时间。没有办法将该状态保存到单元格本身中。您必须在视图控制器中执行此操作,并在 tableView(_:cellForRowAt:)
方法中传递显示的单元格需要跟踪的时间。如果你已经在做,那么这不是问题。
另一个问题是计时器将永远存在(或直到它的单元停止它)。对于某些计时器,如果单元格对象丢失了对它的引用,则没有人会再停止该计时器,因此它只会每秒触发一次,直到应用程序被终止。
此时我不确定计时器是否会使用目标选择器方法保留单元格对象来创建计时器,但无论如何都存在问题:
- 如果计时器保留了单元格对象,则意味着您将发生内存泄漏:这意味着计时器和单元格将永远不会被释放,并将持续使用内存和处理资源;
- 如果计时器不保留单元格对象,那么一旦单元格被释放,可能会发生崩溃。
您应该在 tableView(_:didEndDisplaying:forRowAt)
方法中停止单元格的计时器。此时,单元格正在隐藏,因此计时器不再有意义。
当然,所有这些都会导致另一个问题:保留单元格的时间状态。这种处理应该在呈现表格视图的视图控制器中完成,而不是在表格视图单元格中完成。
TL;DR: 在每个单元格上设置计时器会让您很头疼,事实上,以这种方式处理事情几乎是不可能的。你应该让你的计时器对应于呈现表格视图的视图控制器中的每个单元格,事情会简化很多。