Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EXC_BAD_ACCESS (code=2) when using NSNumberFormatter

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.


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:

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:

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:

Error 1

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).


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

  1. mainDisplay is set successfully in viewDidLoad()
  2. mainDisplay.content is called
  3. in the content's setter the switch-statement executes the .Number case
  4. text and state are successfully set
  5. removeLeadingZeros() is called
  6. the switch-statement accesses content's getter
  7. the switch-statement in content's getter executes the .ShowingNumber case
  8. the if-statements resolve to true, finally trying to evaluate the NSNumberFormatter expression
  9. the EXC_BAD_ACCESS occurs

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!


Here are links to the complete CalculatorController and MainDisplayMicroController.


Update #1:

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

Error 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.

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.

Here's the complete code for the new CalculatorController.


Update #3:

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

Change from NSNumberFormatter

Now I get the following error though:

New Error

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:

Exception

like image 314
Marcus Rossel Avatar asked May 12 '15 13:05

Marcus Rossel


People also ask

Are there crash logs without the EXC_bad_access key identifier?

Occasionally, you could encounter crash logs without the EXC_BAD_ACCESS key identifier, which is most common on macOS and looks as follows: It’s best to look at other details of the crash logs to get an insight into where the crash occurred.

How to identify bad access on Mac OS X?

Within the parenthesis, you can find the SIGSEGV subtype. The second line indicates the type of bad access together with the memory address. Occasionally, you could encounter crash logs without the EXC_BAD_ACCESS key identifier, which is most common on macOS and looks as follows:

What is a bad access exception?

When that block of memory is no longer mapped for your application or, put differently, that block of memory isn't used for what you think it's used, it's no longer possible to access that chunk of memory. When this happens, the kernel sends an exception ( EXC ), indicating that your application cannot access that block of memory ( BAD ACCESS ).

What is EXC_bad_access and how to fix it?

In summary, when you run into EXC_BAD_ACCESS, it means that you try to send a message to a block of memory that can't execute that message. In some cases, however, EXC_BAD_ACCESS is caused by a corrupt pointer. Whenever your application attempts to dereference a corrupt pointer, an exception is thrown by the kernel. 2. Debugging EXC_BAD_ACCESS


1 Answers

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. 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
like image 184
roop Avatar answered Oct 23 '22 19:10

roop