使用超时

时间:2018-05-30 10:21:50

标签: swift

我正在创建聊天机器人应用程序,它模仿真实用户的“输入”状态,因此在每条消息之后必须有1秒(例如)延迟,然后应用程序将发布第二条消息。

我的消息传递逻辑是

    let size = dictionary[key]!.count;
    let pointer = random(lower: 0, upper: UInt32(size) - 1);
    let part = dictionary[key]![pointer];

    for row in part {
        if let message = row as? Message {
            self.sendMessage(message: message);
            print("MESSAGE TRIGGERED");
            usleep(1 * 1000 * 1000)
        }

        if let question = row as? Question {

        }
    }

这里我正在使用随机数组的消息(实际上是字符串),并在一个循环中将它们添加到我的表中,就像这样

func sendMessage(message: Message) {
    self.tableView.beginUpdates();

    let indexPath = IndexPath.init(row: self.log.count, section: 0);
    self.log.insert(
        message,
        at: self.log.count
    );
    self.tableView.insertRows(at: [indexPath], with: UITableViewRowAnimation.fade);
    self.tableView.endUpdates()
    self.tableView.scrollToRow(at: indexPath, at: .bottom, animated: true)
}

问题是,如果消息数组中有10个字符串,我在控制台中看到10个“MESSAGE TRIGGERED”日志,1个新行,每秒,就像我计划的那样,但在表中,我不知道看到任何行10秒钟,然后它们都会立刻出现。为什么呢?

3 个答案:

答案 0 :(得分:1)

您的问题是您正在主线程上运行,并且永远不会让系统有机会更新表。您违反了规则:永远不会在主线程上调用sleep

您可以通过以下几种方式解决此问题:

1)在后台线程上运行循环,并返回主线程以调用self.sendMessageprint

let size = dictionary[key]!.count;
let pointer = random(lower: 0, upper: UInt32(size) - 1);
let part = dictionary[key]![pointer];

DispatchQueue.global().async {
    for row in part {
        if let message = row as? Message {
            DispatchQueue.main.async {
                self.sendMessage(message: message);
                print("MESSAGE TRIGGERED");
            }
            usleep(1 * 1000 * 1000)
        }

        if let question = row as? Question {

        }
    }
}

2)使用间隔为1秒的重复计时器,并在每次计时器触发时向表中添加一个项目。

let size = dictionary[key]!.count;
let pointer = random(lower: 0, upper: UInt32(size) - 1);
let part = dictionary[key]![pointer];

var idx = 0
Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { timer in

    if idx >= part.count {
        timer.invalidate()
    } else {
        let row = part[idx]
        if let message = row as? Message {
            self.sendMessage(message: message);
            print("MESSAGE TRIGGERED");
        }

        if let question = row as? Question {

        }
    }
    idx += 1
}

3)延迟每个表的延迟越来越大:

let size = dictionary[key]!.count;
let pointer = random(lower: 0, upper: UInt32(size) - 1);
let part = dictionary[key]![pointer];

var delay = 0.0
for row in part {
    if let message = row as? Message {
        DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
            self.sendMessage(message: message);
            print("MESSAGE TRIGGERED");
        }
        delay += 1.0
    }

    if let question = row as? Question {

    }
}

答案 1 :(得分:1)

将此代码替换为:

let size = dictionary[key]!.count;
let pointer = random(lower: 0, upper: UInt32(size) - 1);
let part = dictionary[key]![pointer];

for row in part {
    if let message = row as? Message {
        self.sendMessage(message: message);
        print("MESSAGE TRIGGERED");
        usleep(1 * 1000 * 1000)
    }

    if let question = row as? Question {

    }
}

此代码:

let size = dictionary[key]!.count;
let pointer = random(lower: 0, upper: UInt32(size) - 1);
let part = dictionary[key]![pointer];
var timer = Timer!
for row in part {
    if let message = row as? Message {
        timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(runTimedCode), userInfo: nil, repeats: true)
    }

    if let question = row as? Question {

    }
}
timer.invalidate()

还添加此功能:

  @objc func runTimedCode() {
      self.sendMessage(message: message);
        print("MESSAGE TRIGGERED");
  }

答案 2 :(得分:1)

不要睡觉主线。

相反,您在之后设置了一个函数调用。

你应该在每个单元格内创建一个计时器,然后每个单元格都有单独的计时器。

class MyCustomCell: UITableViewCell {
@IBOutlet weak private var myLabel: UILabel!

private var timer: Timer?
private var timeCounter: Double = 0

var expiryTimeInterval: TimeInterval? {
    didSet {
        startTimer()
    }
}

private func startTimer() {
    if let interval = expiryTimeInterval {
        timeCounter = interval // should be 1 for 1 second.
        if #available(iOS 10.0, *) {
            timer = Timer(timeInterval: 1.0,
                          repeats: true,
                          block: { [weak self] _ in
                            guard let strongSelf = self else {
                                return
                            }
                            strongSelf.onComplete()
            })
        } else {
            timer = Timer(timeInterval: 1.0,
                          target: self,
                          selector: #selector(onComplete),
                          userInfo: nil,
                          repeats: true)
        }
    }
}

@objc func onComplete() {
    guard timeCounter >= 0 else {
        timer?.invalidate()
        timer = nil
        return
    }
    myLabel.text = String(format: "%d", timeCounter)
    timeCounter -= 1
}
}

你可以像这样使用;

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell =  tableView.dequeueReusableCell(withIdentifier: "cell") as! MyCustomCell

    cell.expiryTimeInterval = 10

    return cell
}