使用NSNumberFormatter时EXC_BAD_ACCESS(code = 2)

Mar*_*sel 11 exc-bad-access uiviewcontroller uilabel iboutlet swift

我遇到了一个问题,我无法理解为我的生活.我搜索了互联网,试图了解Swifts的EXC_BAD_ACCESS,但似乎没有任何帮助.


以下代码很长,但大多数时候注释都是理解相关项所需的所有信息.

我有一个类CalculatorController,其中包含以下相关方法和属性:

import UIKit    

class CalculatorController: UIViewController {

    // the actual `@IBOutlet` which is never accessed directly
    @IBOutlet private weak var _mainDisplay: UILabel!

    // an instance of `MainDisplayMicroController`
    // holds a reference to `_mainDisplay`
    // is used to manipulate `_mainDisplay` in a controlled way
    private var mainDisplay: MainDisplayMicroController!

    override func viewDidLoad() {
        super.viewDidLoad()

        // connects `mainDisplay` with `_mainDisplay`
        mainDisplay = MainDisplayMicroController(label: _mainDisplay)

        // sets `_mainDisplay`'s `text` property to "0"
        mainDisplay.content = .Number(0)

        //...
    }

    //...
}
Run Code Online (Sandbox Code Playgroud)

为了以_mainDisplay某种方式进行管理,我创建了一个类MainDisplayMicroController,它一方面包含对UILabel自身的引用,另一方面包含对以下内容执行操作的方法和属性UILabel:

import UIKit

class MainDisplayMicroController {

    // used to express what `label.text` is currently showing
    private enum DisplayState {
        case ShowingNumber
        case ShowingConstant
        case ShowingErrorMessage
        case Unknown
    }

    // holds the current state of what `label.text` is showing
    private var state = DisplayState.Unknown

    // used to pass different types of values in and out of this class
    enum ContentType {
        case Number(Double)
        case Constant(String)
        case ErrorMessage(String)
        case Unknown(Any?)
    }

    // holds the reference to the label which is being manipulated/managed
    private var label: UILabel?

    // makes `label`'s `text` property directly accessible, as `label` is `private`
     var text: String? {
        get {
            return label?.text
        }
        set {
            label?.text = newValue
            removeLeadingZeros()
            transformToInteger()
        }
    }

    // a property to allow controlled retrieval and manipulation of `label.text`
    // uses `ContentType` to make clear what the information in `label.text` is/ is supposed to be
    var content: ContentType {
        get {
            switch state {
            case .ShowingNumber:
                if let string = text {
                    if let value = NSNumberFormatter().numberFromString(string)?.doubleValue {
                        return .Number(value)
                    }
                }
            case .ShowingConstant:
                if let symbol = text {
                    return .Constant(symbol)
                }
            case .ShowingErrorMessage:
                if let message = text {
                    return .ErrorMessage(message)
                }
            default:
                break
            }

            state = .Unknown
            return .Unknown(text)
        }
        set {
            switch newValue {
            case .Number(let value):
                text = "\(value)"
                state = .ShowingNumber
                removeLeadingZeros()
                transformToInteger()
            case .Constant(let symbol):
                text = symbol
                state = .ShowingConstant
            case .ErrorMessage(let message):
                text = message
                state = .ShowingErrorMessage
            case .Unknown(let thing):
                text = "Error: Passed unknown value: \(thing)"
                state = .ShowingErrorMessage
            }
        }
    }

    // removes the ".0" from `label.text`, if it is a whole number
    private func transformToInteger() {
        if state == .ShowingNumber {
            switch content {
            case .Number(let value):
                if round(value) == value {
                    var doubleString = "\(value)"

                    if doubleString.rangeOfString("e") == nil {
                        dropLast(doubleString)
                        dropLast(doubleString)
                    }

                    text = doubleString
                }
            default:
                break
            }
        }
    }

    // removes leading "0"s from `label.text` if they are redundant
    private func removeLeadingZeros() {
        if state == .ShowingNumber {
            switch content {
            case .Number(let displayedValue):
                content = .Number(displayedValue)
            default:
                break
            }
        }
    }

    //...
}
Run Code Online (Sandbox Code Playgroud)

现在,当我运行代码时,我收到以下错误:

错误1

从我读过的内容来看EXC_BAD_ACCESS,在尝试调用已发布对象上的方法时经常会出现错误.我试过NSZombie用来检查问题,但我没有找到任何东西(可能是因为我使用时无能NSZombie).


如果我试着按照逻辑发生的事情,我得出以下结论:

  1. mainDisplay 已成功设置 viewDidLoad()
  2. mainDisplay.content 叫做
  3. contentsetter中,switch-statement执行.Numbercase
  4. textstate成功设置
  5. removeLeadingZeros() 叫做
  6. switch-statement访问content的getter
  7. content'getter中的switch语句执行.ShowingNumber大小写
  8. if语句解析为true,最后尝试计算NSNumberFormatter表达式
  9. EXC_BAD_ACCESS发生

有谁知道为什么会这样?是否与我操纵@IBOutlet另一个班级有关?
任何帮助是极大的赞赏!


这里是完整CalculatorControllerMainDisplayMicroController.


更新#1:

正如@abdullah建议我尝试将NSNumberFormatter表达式指向多个表达式.我仍然得到错误:

错误2


更新#2:

我删除了所有引用和外部类,使其尽可能简单,同时保持相同的功能.已
定义的所有方法和属性MainDisplayMicroController已移至CalculatorModel.
这些方法和属性现在可以访问原始文件@IBOutlet,而不是对它的任何引用.

但是当我试图运行它时,我会得到EXC_BAD_ACCESS(code=2)相同的代码.
我只是非常困惑,因为它与奇怪的引用无关,或者对象被释放得太快了.

这是新的完整代码CalculatorController.


更新#3:

我删除了该NSNumberFormatter行,将其更改为:

从NSNumberFormatter更改

现在我收到以下错误:

新错误

我假设代码存在一些基本问题,所以我要废弃它.但感谢所有的帮助,并尝试解决这个问题.


更新#4:

这是我在throw上为所有异常添加断点时得到的结果:

例外

roo*_*oop 6

我真的没有看到任何可能导致崩溃的行.我建议你做以下事情:

  1. 进行干净的构建(清理,核对派生数据文件夹,然后构建)并查看崩溃是否仍然存在
  2. 如果崩溃仍然存在,请为所有异常设置一个断点,以查看callstack中的哪个操作导致崩溃,并从那里获取


Dun*_*n C 0

@WarrenBurton 正在做一些事情。

把你的大班崩溃的线拿出来,在操场上运行它,它工作得很好:

let string = "1.213"
if let value = NSNumberFormatter().numberFromString(string)?.doubleValue 
{
  println("value = \(value)")
}
Run Code Online (Sandbox Code Playgroud)

显示结果

值 = 1.213

你的变量“string”在你的类中定义在哪里?