Таинственные сбои в Swift 1.2 - только в версиях Release

После обновления до Xcode 6.3 (beta 1) и Swift 1.2 все мои приложения загадочно сбой в версиях Release. Они отлично работают после обновления моего кода для Swift 1.2 в сборках Debug. Отладчик не имеет смысла, где происходят сбои, и непонятно, почему. Некоторые из аварий -

malloc: *** ошибка для объекта 0x7ff0c3824800: указатель освобожден не был назначен *** установить точку останова в malloc_error_break для отладки

Другие - это "непризнанный селектор", но они не имеют никакого смысла; объекты, к которым отправляются селектора, - это даже не объекты, о которых я знаю. Похоже, что что-то пошло не так с управлением памятью, так что один объект заменяется другим.

Что может быть причиной этого? Ничего полезного в стеке вызовов (так что я даже не знаю, где в моем коде происходит сбой), и никаких переменных, появляющихся на панели переменных отладчика при прохождении моего кода (чтобы я не мог даже посмотрите на ценности вещей), как я мог даже начать отслеживать его?

Ответ 1

Удивительно, но я проследил это, главным образом, удалив код в больших образцах, пока я не дошел до этого (это контроллер вида):

class LessonListController: UIViewController {
    var terms : [Term]
    // var terms : NSArray
    init(terms data:NSArray) {
        let arr = data.sortedArrayUsingDescriptors([NSSortDescriptor(key: "lessonSection", ascending: true)])
        self.terms = arr as! [Term]
        // self.terms = arr
        super.init(nibName:"LessonList", bundle:nil)
    }
    required init(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    @IBAction func doDismiss(sender: AnyObject) {
        self.dismissViewControllerAnimated(true, completion: nil)
    }
}

Если (в сборке Release) мы представляем этот контроллер представления, а затем увольняем его, мы выходим из командной строки dealloc, что доказывает мою теорию о том, что это проблема управления памятью.

Выделив код, я смог попробовать различные альтернативы. Ясно, что проблема заключается в свойстве var terms : [Term] (потому что единственное, что Swift делает под капотом в dealloc, выпускает этот массив). Значение этого свойства, как вы можете видеть в моем init, представляет собой NSArray, который появился из Cocoa (thru sortedArrayUsingDescriptors) и был перенесен в массив Swift. Судом и ошибкой я обнаружил:

  • Если мы изменим реализацию так, чтобы это свойство было NSArray (см. альтернативные строки с комментариями), мы не сбой.

  • Или, если мы не сортируем (так что этот NSArray не приходит из Cocoa), мы не сбой.

  • Или (дождитесь его), если мы заменим self.terms = arr as! [Term] на self.terms = arr as NSArray as! [Term], мы не сработаем!

Но эта третья альтернатива - обходной путь. Я просмотрел весь свой код во всех своих приложениях, ищущих приведения as! [SomeType] и заменив их на as NSArray as [SomeType], и все мои сбои исчезли.

Моя теория заключается в том, что что-то не так с управлением памятью Swift в оптимизированной версии релиза только в очень конкретной ситуации, когда NSArray прибывает из Cocoa и соединяется для нас с [AnyObject], прежде чем наш код сможет ухватиться из этого. Такой NSArray не пересекает мост должным образом. Но, перейдя в NSArray, а затем обратно вниз до конкретного массива [SomeType] Swift, проблема решена.

Естественно, я предполагаю, что, когда Apple это выяснит, они исправит его, и тогда мы можем прекратить использовать этот обходной путь. Но до тех пор мои приложения снова запускаются в сборке Release.