Swift 2 到 3 迁移错误(libc++abi.dylib:以 NSException 类型的未捕获异常终止)

Posted

技术标签:

【中文标题】Swift 2 到 3 迁移错误(libc++abi.dylib:以 NSException 类型的未捕获异常终止)【英文标题】:Swift 2 to 3 Migration Error (libc++abi.dylib: terminating with uncaught exception of type NSException) 【发布时间】:2016-10-15 05:19:29 【问题描述】:

我的一个控制器奇怪地导致运行时错误崩溃,而它在 Swift 2 中却没有。

我得到的错误是。

libc++abi.dylib:以 NSException 类型的未捕获异常终止

我附上了一个屏幕截图,显示我已经连接了所有界面构建器元素。

我做了一些实验来查看错误发生的时间点,我认为它可能与设置 textView 有关,但不确定。

1.) 注意控制台消息在print("Draw Undo html") 之后停止。我最初认为该错误与 after 行有关,因为我从未见过控制台消息print("Set attributed text accordingly")

2.) 如果我注释掉 incrementListTextView.attributedText = html.html2AttributedString 行,结果是这样的

错误仍然会发生,只是在它发生之前运行其余代码。很奇怪,但是看到错误似乎首先出现在该行周围,我认为它与 TextView 有关,但不确定它可能是什么。

我也附上了这个场景的图片。

InventoryItemController.Swift(完整文件参考)

//
//  InventoryItemController.swift
//  Inventory Counter
//
//  Created by Joseph Astrahan on 4/3/16.
//  Copyright © 2016 Joseph Astrahan. All rights reserved.
//

import UIKit
import CoreData

class InventoryItemController: UIViewController, UITextFieldDelegate 

    var inventoryItem : Inventory?
    var m_incrementAmount = 0 //amount it will increment everytime you press (+)
    var m_itemHistory = [Int]() //create array to store undo/redo history
    var m_undoIndex = 0


    @IBOutlet weak var inventoryBarCodeLabel: UILabel!

    @IBOutlet weak var inventoryTotalLabel: UILabel!

    //List of increments
    @IBOutlet weak var incrementListTextView: UITextView!

    //Amount to increment by
    @IBOutlet weak var incrementAmountTextField: UITextField!

    @IBOutlet weak var inventoryNameNavItem: UINavigationItem!

    @IBAction func resetBarButtonAction(_: UIBarButtonItem) 
        //Present 'Are you sure?' Dialog & Reset Vars.

        // create the alert in local scope (no need for weak or unowned reference to self in closures)
        let alert = UIAlertController(title: "Are You Sure?", message: "This will delete all the counts you have done.", preferredStyle: UIAlertControllerStyle.alert)

        // add an action (button)
        alert.addAction(UIAlertAction(title: "Yes", style: UIAlertActionStyle.default, handler:  action in
            self.resetTotals()
        ))

        alert.addAction(UIAlertAction(title: "Cancel", style: UIAlertActionStyle.cancel, handler: nil))

        // show the alert
        self.present(alert, animated: true, completion: nil)

    

    func resetTotals()
        print("resetting...")
        m_itemHistory = [Int]()
        m_itemHistory.append(0)//first count is always 0 after reset.

        m_undoIndex = 0 //set back to 0

        updateTotal()//update total and save to disk.

        print("reset!...")
    

    @IBAction func addInventoryButtonAction(_: UIButton) 

        //When you add you have to first trim the array of everything that was after it.  The redo history is now gone.
        let slice = m_itemHistory[0...m_undoIndex]
        m_itemHistory = Array(slice)//create new int array from the slice

        m_incrementAmount = Int(incrementAmountTextField.text!)!
        m_itemHistory.append(m_incrementAmount)

        m_undoIndex = m_undoIndex + 1 //add to the index, because undo always happens from last added~

        //Update addCount on actual inventoryItem (save to database)
        updateTotal()
    

    @IBAction func undoButtonAction(_: UIButton) 
        print("undo")

        m_undoIndex = m_undoIndex - 1

        updateTotal()
    

    @IBAction func redoButtonAction(_: UIButton) 
        print("redo")

        m_undoIndex = m_undoIndex + 1

        updateTotal()
    


    func textFieldShouldReturn(_ textField: UITextField) -> Bool 
        textField.resignFirstResponder()
        return true
    

    func textFieldShouldEndEditing(_ textField: UITextField) -> Bool 
        textField.resignFirstResponder()
        return true
    

    func textFieldDidEndEditing(_ textField: UITextField)
       textField.resignFirstResponder()
    

    func dismissKeyboard() 
        //Causes the view (or one of its embedded text fields) to resign the first responder status.
        view.endEditing(true)
    

    override func viewDidLoad() 
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(LoginViewController.dismissKeyboard))
        self.view.addGestureRecognizer(tap)

        print("Showing Inventory Item In Question")
        print(inventoryItem)
        print("Inventory Name="+(inventoryItem?.name!)!)

        //inventoryNameLabel.text = inventoryItem.name!
    

    override func viewWillAppear(_ animated: Bool) 
        super.viewWillAppear(animated)

        //add observer for contentSize so it centers vertically
        incrementListTextView.addObserver(self, forKeyPath: "contentSize", options: NSKeyValueObservingOptions.new, context: nil)

        //Draw inventory name & barcode
        inventoryNameNavItem.title = inventoryItem?.name
        inventoryBarCodeLabel.text = inventoryItem?.barcode

        //Load first time only when view appears to set default
        m_itemHistory.append(Int((inventoryItem?.addCount!)!))//add initial value of last 'count'

        m_undoIndex = 0 //reset to start.

        updateTotal() //updates total to screen

        print("Finished UpdateTotal, Should be Showing Screen Now")

    

    func updateTotal()

        //Get the max Index we can count to -1 (because arrays are 0 indexed)
        let historyTotalMaxIndex = m_itemHistory.count - 1
        print("historyTotalCheck: HistoryTotalMaxIndex=\(historyTotalMaxIndex)")

        //Current state of undoIndex
        print("Init: m_undoIndex =\(m_undoIndex)")

        //Do checks to prevent out of index bounds.
        if(m_undoIndex<0)
            m_undoIndex = 0
        

        if(m_undoIndex>historyTotalMaxIndex)
            m_undoIndex = historyTotalMaxIndex
        

        //After modifying...
        print("Current: m_undoIndex =\(m_undoIndex)")


        //Draw HTML
        var html = "<html><font size=\"5\"><font color=\"#008800\"><center>"
        for index in 0...m_undoIndex 
            let increment = m_itemHistory[index]
            html = html + "+\(increment), "
        
        html = html + "</center></font></font></html>"

        print(html)

        print("Draw Undo HTML")

        incrementListTextView.attributedText = html.html2AttributedString

        print("Set attributed text accordingly")

        //get sum of itemHistory

        let slice = m_itemHistory[0...m_undoIndex] //returns slice of the array we want.
        let sumArray = Array(slice)//create new int array from the slice

        print("SumArray Created")

        let sum = sumArray.reduce(0, +) //now we can sum up that new sliced array
        inventoryItem?.addCount = sum as NSNumber? //set the add count

        print("Reduced the Array")

        //reset html to different now
        html = "<font size=\"10\"><center>Current: \(inventoryItem?.currentCount!) , <font color=\"#008800\"> Counted: \(inventoryItem?.addCount!)</font></center></font>"

        inventoryTotalLabel.attributedText = html.html2AttributedString

        print("save the context")
        //Save the changes
        (UIApplication.shared.delegate as! AppDelegate).saveContext()

    

    override func viewWillDisappear(_ animated: Bool) 
        super.viewWillDisappear(animated)
        //have to remove or will crash
        incrementListTextView.removeObserver(self, forKeyPath: "contentSize")
    

    /// Force the text in a UITextView to always center itself.
    func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutableRawPointer) 
        let textView = object as! UITextView
        var topCorrect = (textView.bounds.size.height - textView.contentSize.height * textView.zoomScale) / 2
        topCorrect = topCorrect < 0.0 ? 0.0 : topCorrect;
        textView.contentInset.top = topCorrect
    



    override func didReceiveMemoryWarning() 
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    





extension String 
    var html2AttributedString: NSAttributedString? 
        guard let data = data(using: .utf8) else  return nil 
        do 
            return try NSAttributedString(data: data, options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: String.Encoding.utf8.rawValue], documentAttributes: nil)
         catch let error as NSError 
            print(error.localizedDescription)
            return  nil
        
    
    var html2String: String 
        return html2AttributedString?.string ?? ""
    

【问题讨论】:

你是否在项目中添加了 libc++.tbd 文件? 我该怎么做?或者我怎么知道我是否这样做了? 试试这个可能会解决它。单击您的项目 -> 常规 -> 链接框架和二进制文件 -> 单击 ON + 并搜索 libc++.tbd -> 添加 仍然错误,我认为问题不是库,因为库正在运行,它的库因 NSException 而崩溃。 用调试检查项目,我认为它是获取图像的问题。在您的错误日志中显示“获取图像时出错” 【参考方案1】:

您好,我发现了您的问题并在此处修复。

问题是您从未遵循 Swift 3 风格的 KVO 方法调用 从 Swift 3 开始,方法原型发生了变化。

您的代码在这里:

func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutableRawPointer) 
        let textView = object as! UITextView
        var topCorrect = (textView.bounds.size.height - textView.contentSize.height * textView.zoomScale) / 2
        topCorrect = topCorrect < 0.0 ? 0.0 : topCorrect;
        textView.contentInset.top = topCorrect

但新原型如下:

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) 
        let textView = object as! UITextView
        var topCorrect = (textView.bounds.size.height - textView.contentSize.height * textView.zoomScale) / 2
        topCorrect = topCorrect < 0.0 ? 0.0 : topCorrect;
        textView.contentInset.top = topCorrect
    

你看到这里有什么不同吗?

我还发现了另一个问题: 您确实在 viewWillAppear 函数中添加了观察者,请将其移至 viewDidLoad 函数,并将 removeObserver 代码移至 deinit 或覆盖 func didReceiveMemoryWarning()。

代码如下:

deinit 
        incrementListTextView.removeObserver(self, forKeyPath: "contentSize")
    

希望对你有很大帮助!

【讨论】:

是的,解决了它。哇,绝对不会猜到所有这些问题! 非常感谢您的赞赏!

以上是关于Swift 2 到 3 迁移错误(libc++abi.dylib:以 NSException 类型的未捕获异常终止)的主要内容,如果未能解决你的问题,请参考以下文章

Swift 2.3 到 Swift 3.1 迁移 - Alamofire 请求错误

从Swift 3.2迁移到Swift 4我使用autosavesInPlace收到错误

“无下标成员”错误(迁移到 Swift 3)

Swift 2 到 Swift 3 迁移:对成员“下标”的模糊引用

迁移到 Swift 3.0,构建问题

迁移到 Swift 3 时可以使用旧框架吗?