Подтвердить что ты не робот

EXC_BAD_ACCESS (код = 2) при использовании NSNumberFormatter

У меня проблема, которую я не могу понять для жизни меня. Я искал интернет, пытаясь понять 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
            }
        }
    }

    //...
}

Теперь, когда я запускаю код, я получаю следующую ошибку:

Error 1

Из того, что я читал в 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 в несколько выражений. Я все еще получаю сообщение об ошибке:

Error 2


Обновление # 2:

Я удалил все ссылки и внешние классы, чтобы сделать его максимально простым, сохраняя при этом ту же функциональность.
Все методы и свойства, определенные в MainDisplayMicroController, были перенесены на CalculatorModel.
Эти методы и свойства теперь получают доступ к исходному @IBOutlet, а не к какой-либо ссылке на него.

Но при попытке запустить его я получаю EXC_BAD_ACCESS(code=2) в той же строке кода.
Я просто очень смущен, потому что он не может иметь ничего общего со странными ссылками или объектами, выпущенными слишком скоро.

Здесь полный код для нового CalculatorController.


Обновление № 3:

Я удалил строку NSNumberFormatter, изменив ее на:

Change from NSNumberFormatter

Теперь я получаю следующую ошибку:

New Error

Я предполагаю, что есть некоторая фундаментальная проблема с кодом, поэтому я отказываюсь от него. Но спасибо за всю помощь и попытаться понять это.


Обновление # 4:

Это то, что я получаю при добавлении точки останова при броске для всех исключений:

Exception

4b9b3361

Ответ 1

Я не вижу ничего в этой строке, которая может вызвать сбой. Я предлагаю вам сделать следующее:

Ответ 2

@WarrenBurton находится на чем-то.

Возьмите свою линию, которая вылетает из вашего большого класса и запускает ее на игровой площадке, и она отлично работает:

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

Отображает результат

value = 1.213

Где ваша переменная "строка", определенная в вашем классе?

Ответ 3

Обратите внимание, что строка синяя, как ключевое слово, а не черная, как и другие локальные переменные.

Я бы попробовал локальную переменную string == > myString просто знать наверняка.

Ответ 4

Просто потому, что я видел одно и то же, и заметил, что никто не прокомментировал ваше последнее редактирование (и, возможно, его коллега по Googler для этого вопроса увидит это когда-нибудь):

Для обеих наших ситуаций проблема - бесконечная рекурсия - мы вызываем метод из себя бесконечно. Это ошибка. Импликация при крушении NSNumberFormatter - красная селедка.