Swift 2 到 3 迁移错误(libc++abi.dylib:以 NSException 类型的未捕获异常终止)
Swift 2 to 3 Migration Error (libc++abi.dylib: terminating with uncaught exception of type NSException)
我的一个控制器在 Swift 2.
中不存在时奇怪地导致运行时错误崩溃
我得到的错误是。
libc++abi.dylib:以 NSException
类型的未捕获异常终止
我附上了一张屏幕截图,显示我已连接所有界面构建器元素。
我做了一些实验,看看错误发生在什么时候,我认为它可能与设置 textView 有关,但不确定。
1.) 注意控制台消息在 print("Draw Undo HTML")
后停止。我最初认为错误与之后的行有关,因为我从未看到控制台消息 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 ?? ""
}
}
您好,我发现了您的问题并已在此处解决。
问题是你从来没有遵循过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.
中不存在时奇怪地导致运行时错误崩溃我得到的错误是。
libc++abi.dylib:以 NSException
类型的未捕获异常终止我附上了一张屏幕截图,显示我已连接所有界面构建器元素。
我做了一些实验,看看错误发生在什么时候,我认为它可能与设置 textView 有关,但不确定。
1.) 注意控制台消息在 print("Draw Undo HTML")
后停止。我最初认为错误与之后的行有关,因为我从未看到控制台消息 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 ?? ""
}
}
您好,我发现了您的问题并已在此处解决。
问题是你从来没有遵循过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")
}
希望对您有所帮助!