使用NSNumberFormatter时,EXC_BAD_ACCESS(代码=2)

时间:2022-09-06 19:26:02

I'm having a problem, which I can't figure out for the life of me. I've searched the internet, trying to understand Swifts's EXC_BAD_ACCESS, but nothing seemed to help.

我有个问题,我一辈子也搞不清楚。我在互联网上搜索,试图理解Swifts的EXC_BAD_ACCESS,但似乎没有任何帮助。


The following code is quite long, but most of the time the comments are all the information needed to understand the item of relevance.

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

I have a class CalculatorController, which contains the following relevant methods and properties:

我有一个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)

        //...
    }

    //...
}

In order to manage _mainDisplay in a certain way, I have created a class MainDisplayMicroController, which on the one hand contains a reference to the the UILabel itself, and on the other hand contains methods and properties, which perform actions on the UILabel:

为了以某种方式管理_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
            }
        }
    }

    //...
}

Now, when I run the code I get the following error:

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

使用NSNumberFormatter时,EXC_BAD_ACCESS(代码=2)

From what I've read on EXC_BAD_ACCESS, the error often occurs when trying to call methods on released objects. I've tried using NSZombieto check the issue, but I didn't find anything (probably due to my incompetence when using NSZombie).

从我在EXC_BAD_ACCESS中看到的内容来看,当尝试调用释放对象上的方法时,常常会出现错误。我尝试过使用NSZombieto检查问题,但没有发现任何问题(可能是由于我在使用NSZombie时的无能)。


If I try to follow what is happening by logic, I come to following conclusion:

如果我试着用逻辑去理解正在发生的事情,我会得出以下结论:

  1. mainDisplay is set successfully in viewDidLoad()
  2. 在viewDidLoad()中成功设置主显示器
  3. mainDisplay.content is called
  4. mainDisplay。内容被称为
  5. in the content's setter the switch-statement executes the .Number case
  6. 在内容的setter中,开关语句执行.Number实例。
  7. text and state are successfully set
  8. 成功设置文本和状态
  9. removeLeadingZeros() is called
  10. removeLeadingZeros()
  11. the switch-statement accesses content's getter
  12. 切换语句访问内容的getter
  13. the switch-statement in content's getter executes the .ShowingNumber case
  14. content的getter中的switch语句执行.ShowingNumber案例
  15. the if-statements resolve to true, finally trying to evaluate the NSNumberFormatter expression
  16. if语句解析为true,最后尝试计算NSNumberFormatter表达式
  17. the EXC_BAD_ACCESS occurs
  18. EXC_BAD_ACCESS发生

Does anyone know why this is happening? Does it have to do with me manipulating an @IBOutlet in a different class?
Any help is greatly appreciated!

有人知道这是为什么吗?这是否与我在另一个类中操作一个@IBOutlet有关?非常感谢您的帮助!


Here are links to the complete CalculatorController and MainDisplayMicroController.

这里是到完整CalculatorController和MainDisplayMicroController的链接。


Update #1:

As @abdullah suggested I have tried directing the NSNumberFormatter expression in to multiple expressions. I still get the error though:

正如@abdullah所说,我尝试将NSNumberFormatter表达式定向到多个表达式。但我还是得到了一个错误:

使用NSNumberFormatter时,EXC_BAD_ACCESS(代码=2)


Update #2:

I've removed all references and external classes, to make it as simple as possible, while maintaining the same functionality.
All of the methods and properties defined in MainDisplayMicroController have been moved to CalculatorModel.
These methods and properties now access the original @IBOutlet, not any reference to it.

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

But still when trying to run it I get EXC_BAD_ACCESS(code=2) at the same line of code.
I'm just super confused, as it can't have anything to do with weird references, or objects being released too soon anymore.

但是当尝试运行它时,我仍然在同一行代码中获得EXC_BAD_ACCESS(代码=2)。我只是非常困惑,因为它与奇怪的引用或对象发布得太快没有任何关系。

Here's the complete code for the new CalculatorController.

这是新CalculatorController的完整代码。


Update #3:

I've removed the NSNumberFormatter line, by changing it to:

我将NSNumberFormatter行改为:

使用NSNumberFormatter时,EXC_BAD_ACCESS(代码=2)

Now I get the following error though:

现在我得到以下错误:

使用NSNumberFormatter时,EXC_BAD_ACCESS(代码=2)

I assume there's some fundamental problem with the code, so I'm scrapping it. But thanks for all the help, and attempts at figuring this out.

我认为这段代码有一些基本的问题,所以我将它删除。但谢谢你的帮助,并试图解决这个问题。


Update #4:

This is what I get when adding a breakpoint on throw for all exceptions:

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

使用NSNumberFormatter时,EXC_BAD_ACCESS(代码=2)

4 个解决方案

#1


6  

I don't really see anything in that line that can cause a crash. I suggest you do the following:

在这条线上我没有看到任何可能导致崩溃的东西。我建议你做以下事情:

  1. Make a clean build (clean, nuke your derived data folder, then build) and see if the crash persists
  2. 做一个干净的构建(清理,使用你的衍生数据文件夹,然后构建),看看崩溃是否持续
  3. If the crash persists, set a breakpoint on throw for all exceptions to see which operation in the callstack caused the crash, and take it from there
  4. 如果崩溃持续,那么在抛出时为所有异常设置一个断点,以查看调用堆栈中的哪个操作导致了崩溃,并从那里获取它

#2


0  

@WarrenBurton is on to something.

@WarrenBurton发现了什么。

Take your line that crashes out of your big class and run it in the playground and it works fine:

把你的线从你的大班跑出来,在操场上运行它,它很好:

let string = "1.213"
if let value = NSNumberFormatter().numberFromString(string)?.doubleValue 
{
  println("value = \(value)")
}

Displays the result

显示结果

value = 1.213

值= 1.213

Where is your variable "string" defined in your class?

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

#3


0  

Notice that string is blue, like a keyword, not black, like other local variables.

注意,字符串是蓝色的,像关键字,而不是黑色的,像其他局部变量一样。

I'd try local variable string ==> myString just to know for sure.

我试试局部变量string => myString来确定。

#4


0  

Just 'cuz I was seeing the same thing and noticed no one had commented past your last edit (and maybe a fellow Googler for this issue will see this someday):

因为我看到了同样的事情,并且注意到没有人在你最后一次编辑后评论过(也许有一天这个问题的google同事会看到):

For both of our situations the issue is infinite recursion - we're calling a method from itself infinitely. That's the bug. The implication in the crash of NSNumberFormatter is a red herring.

对于这两种情况,问题都是无限递归——我们从自身调用一个方法。这是错误。NSNumberFormatter崩溃的含义是转移注意力的。

#1


6  

I don't really see anything in that line that can cause a crash. I suggest you do the following:

在这条线上我没有看到任何可能导致崩溃的东西。我建议你做以下事情:

  1. Make a clean build (clean, nuke your derived data folder, then build) and see if the crash persists
  2. 做一个干净的构建(清理,使用你的衍生数据文件夹,然后构建),看看崩溃是否持续
  3. If the crash persists, set a breakpoint on throw for all exceptions to see which operation in the callstack caused the crash, and take it from there
  4. 如果崩溃持续,那么在抛出时为所有异常设置一个断点,以查看调用堆栈中的哪个操作导致了崩溃,并从那里获取它

#2


0  

@WarrenBurton is on to something.

@WarrenBurton发现了什么。

Take your line that crashes out of your big class and run it in the playground and it works fine:

把你的线从你的大班跑出来,在操场上运行它,它很好:

let string = "1.213"
if let value = NSNumberFormatter().numberFromString(string)?.doubleValue 
{
  println("value = \(value)")
}

Displays the result

显示结果

value = 1.213

值= 1.213

Where is your variable "string" defined in your class?

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

#3


0  

Notice that string is blue, like a keyword, not black, like other local variables.

注意,字符串是蓝色的,像关键字,而不是黑色的,像其他局部变量一样。

I'd try local variable string ==> myString just to know for sure.

我试试局部变量string => myString来确定。

#4


0  

Just 'cuz I was seeing the same thing and noticed no one had commented past your last edit (and maybe a fellow Googler for this issue will see this someday):

因为我看到了同样的事情,并且注意到没有人在你最后一次编辑后评论过(也许有一天这个问题的google同事会看到):

For both of our situations the issue is infinite recursion - we're calling a method from itself infinitely. That's the bug. The implication in the crash of NSNumberFormatter is a red herring.

对于这两种情况,问题都是无限递归——我们从自身调用一个方法。这是错误。NSNumberFormatter崩溃的含义是转移注意力的。