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:
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 NSZombie
to 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:
mainDisplay
is set successfully in viewDidLoad()
mainDisplay.content
is calledcontent
's setter the switch-statement executes the .Number
casetext
and state
are successfully setremoveLeadingZeros()
is calledcontent
's gettercontent
's getter executes the .ShowingNumber
caseNSNumberFormatter
expressionEXC_BAD_ACCESS
occursDoes 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
.
As @abdullah suggested I have tried directing the NSNumberFormatter
expression in to multiple expressions. I still get the error though:
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
.
I've removed the NSNumberFormatter
line, by changing it to:
Now I get the following error though:
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.
This is what I get when adding a breakpoint on throw for all exceptions:
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.
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:
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 ).
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
I don't really see anything in that line that can cause a crash. I suggest you do the following:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With