У меня проблема, которую я не могу понять для жизни меня. Я искал интернет, пытаясь понять 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` `text` property to "0"
mainDisplay.content = .Number(0)
//...
}
//...
}
Чтобы управлять _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` `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
}
}
}
//...
}
Теперь, когда я запускаю код, я получаю следующую ошибку:
Из того, что я читал в EXC_BAD_ACCESS
, часто возникает ошибка при попытке вызвать методы для выпущенных объектов. Я попытался использовать NSZombie
, чтобы проверить проблему, но я ничего не нашел (возможно, из-за моей некомпетентности при использовании NSZombie
).
Если я попытаюсь следить за тем, что происходит по логике, я пришел к следующему выводу:
-
mainDisplay
успешно установлен вviewDidLoad()
-
mainDisplay.content
называется - в установщике
content
оператор switch выполняет.Number
case -
text
иstate
успешно установлены -
removeLeadingZeros()
называется - оператор switch получает доступ к
content
getter - оператор switch в
content
getter выполняет.ShowingNumber
case - if-statements разрешают true, наконец, пытаясь оценить выражение
NSNumberFormatter
-
EXC_BAD_ACCESS
происходит
Кто-нибудь знает, почему это происходит? Это связано с тем, что я манипулировал @IBOutlet
в другом классе?
Любая помощь очень ценится!
Вот ссылки на полный CalculatorController
и MainDisplayMicroController
.
Обновление # 1:
Как предложил @abdullah, я попытался направлять выражение NSNumberFormatter
в несколько выражений. Я все еще получаю сообщение об ошибке:
Обновление # 2:
Я удалил все ссылки и внешние классы, чтобы сделать его максимально простым, сохраняя при этом ту же функциональность.
Все методы и свойства, определенные в MainDisplayMicroController
, были перенесены на CalculatorModel
.
Эти методы и свойства теперь получают доступ к исходному @IBOutlet
, а не к какой-либо ссылке на него.
Но при попытке запустить его я получаю EXC_BAD_ACCESS(code=2)
в той же строке кода.
Я просто очень смущен, потому что он не может иметь ничего общего со странными ссылками или объектами, выпущенными слишком скоро.
Здесь полный код для нового CalculatorController
.
Обновление № 3:
Я удалил строку NSNumberFormatter
, изменив ее на:
Теперь я получаю следующую ошибку:
Я предполагаю, что есть некоторая фундаментальная проблема с кодом, поэтому я отказываюсь от него. Но спасибо за всю помощь и попытаться понять это.
Обновление # 4:
Это то, что я получаю при добавлении точки останова при броске для всех исключений: