【发布时间】: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 而崩溃。
-
用调试检查项目,我认为它是获取图像的问题。在您的错误日志中显示“获取图像时出错”
标签: ios swift2 swift3 ios10 nsexception