【问题标题】:Swift fatal error: 'try!' expression unexpectedly raised an error: Error Domain=NSCocoaErrorDomain Code=3840 "Garbage at end."Swift 致命错误:“试试!”表达式意外引发错误:Error Domain=NSCocoaErrorDomain Code=3840 "Garbage at end."
【发布时间】:2017-05-30 09:37:08
【问题描述】:

我的 swift 程序一直出现这个错误,我使用 PHP 和 MySQL 作为数据库。我要将数据从数据库显示到表格视图。它以前可以工作,但是在将它运行到模拟器几次之后,我遇到了同样的错误

class Home: UIViewController, UITableViewDelegate, UITableViewDataSource {

@IBOutlet weak var btnaddclass: UIButton!
@IBOutlet var tableView: UITableView!
   var values: NSArray = []

func get(){
    let url = NSURL(string: "http://localhost/show_db.php")
    let data = NSData(contentsOf: url! as URL)
    values = try! JSONSerialization.jsonObject(with: data! as Data, options:JSONSerialization.ReadingOptions.mutableContainers)as! NSArray
    tableView.reloadData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return values.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! SpecialCell
    let maindata = values[indexPath.row] as! [String:AnyObject]

    cell.Lblclasstitle.text = maindata["class_title"] as? String
    cell.Lblschool.text = maindata["school"] as? String
    cell.Lblsubject.text = maindata["subject"] as? String
    return cell
}
   override func viewDidLoad() {
    self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
    get()


}
override func viewWillAppear(_ animated: Bool) {
    self.tableView.reloadData()
}
}

【问题讨论】:

  • 很相似,试试safe way: do try catch!
  • 当返回的数据不是有效的 JSON 时会发生这种情况。显示返回数据的文本表示。还有一点,你不应该在主线程中调用NSData.init(contentsOf:)
  • 这么多! - 崩溃是您的编码风格的预期行为。

标签: ios swift swift3.0.2


【解决方案1】:

正如其他一些答案所指出的那样,问题是您正在强制 JSON 反序列化并假设它始终 100% 的时间都可以工作。这几乎总是不是一个好的选择。有两种方法可以解决这个问题:

1) 使用试试?而是

如果您尝试执行的语句失败,这可能是一个不错的选择,那么您将获得 nil,然后可以根据它做一些事情。例如,您可以将 get() 函数更改为:

func get(){
    //remove NS prefix and unwrap optional
    guard let url = URL(string: "http://localhost/show_db.php") else { return }

    //remove ! from url and unwrap optional
    guard let data = try? Data(contentsOf: url) else { return }

    //try to deserialize. The return value is of type Any
    guard let deserializedValues = try? JSONSerialization.jsonObject(with: data) else { return }

    //Convert to array of dictionaries
    guard let arrayOfDictionaryValues = deserializedValues as? [[String:String]] else { return }

    //If you make it here, that means everything is ok
    values = arrayOfDictionaryValues
    
    tableView.reloadData()
}

请注意,每个守卫语句都为您提供了执行某些操作并返回的机会。也许您想显示一条消息,说明存在网络错误。

2) 使用 do try catch 块。

我认为使用 try?更适合您的情况,但在某些情况下,do try catch 块可能是合适的。例如:

func getWithErrorHandling() {
    guard let url = URL(string: "http://localhost/show_db.php") else { return }

    do {
        let data = try Data(contentsOf: url)
        let deserializedValues = try JSONSerialization.jsonObject(with: data)
        guard let arrayOfDictionaryValues = deserializedValues as? [[String:String]] else { return }
        values = arrayOfDictionaryValues
    } catch {
        //You should separate the different catch blocks to handle the different types of errors that occur
        print("There was an error")
    }
}

您的整体方法还可以进行其他改进,但由于您是初学者(如您所述),最好一次学习一件事。

另外,正如其他人所提到的,您不应该从主线程获取数据。请改用 URLSession。这篇文章就是一个很好的例子:

Correctly Parsing JSON in Swift 3

【讨论】:

  • 感谢@Guillermo Alvarez!很大的帮助。我会试试你的建议。
【解决方案2】:

1. 不要在 Swift3 中使用 NS... 类。例如:

let url = URL(string: "http://localhost/show_db.php")
let data = Data(contentsOf: url!)

2.使用 do-try-catch 处理异常。

do {
    try ...
} catch {
    // handle exception
}

3. 不要使用as!,除非您确定它是哪个类。使用这个:

if let array = some as? Array {
    // use array now
}

【讨论】:

  • 太棒了!这是一个很大的帮助,因为我只是新手。我尝试了你的建议,但它输出了这个问题:无法读取数据,因为它的格式不正确。
  • @EnriqueJimmarC.Panti JSONSerialization.jsonObject(... 仅适用于 JSON 格式。
  • @EnriqueJimmarC.Panti 如果您将尝试反序列化的数据打印到控制台,它可能会有所帮助。我的猜测是来自服务器的 JSON 无效。
【解决方案3】:

你的代码充满了问题。

“试试!”如果您正在调用的代码引发异常,则表达式会崩溃。这就是你崩溃的原因。不要那样做,尤其是在处理来自外部来源的数据时。请改用do { try.. } catch { }。在 Apple 的文档和在线中有大量的 swift try/catch 块示例。

您收到的错误消息告诉您出了什么问题。您在 JSON 数据流的末尾收到垃圾字符。将您的数据流转换为字符串并记录下来。

也不要使用as!,除非您确定可以将对象转换为所需的类型。 (强制施法失败时会崩溃。)

正如@OOPer 在他们的评论中所说,您不应该从主线程上的 URL 加载数据。这会锁定 UI 直到加载完成,如果加载时间过长,可能会导致您的应用程序被终止。你应该使用像URLSession这样的异步方法。

【讨论】:

  • 我只是从教程中复制的。哈哈,谢谢@Duncan C,我对异步方法不太熟悉。我只是新手。你能告诉我更多吗?
  • 看看我对这个问题的回答:stackoverflow.com/questions/41262793/…
  • 如果您从教程中复制了该代码,您应该找到另一个教程。该代码就像是不良编码习惯和潜在崩溃的雷区。能发个教程的链接吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-06
  • 2016-04-13
  • 1970-01-01
  • 1970-01-01
  • 2020-01-26
  • 1970-01-01
相关资源
最近更新 更多