быстрый сеттер, вызывающий ex_bad_access

У меня есть простой класс ниже

import Foundation

public class UsefulClass: NSObject{
    var test:NSNumber{
        get{return self.test}
        set{
            println(newValue)
            self.test = newValue
        }
    }
    override init() {
        super.init()
        self.test = 5;
    }
}

и я инициализирую его здесь

import UIKit

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        var testClass = UsefulClass()
    }
}

Но это приводит к тому, что xcode печатает 200 5 с, а затем происходит сбой из-за кода EXC_BAD_ACCESS = 2. Почему это происходит?


person user3211122    schedule 03.07.2015    source источник


Ответы (3)


@vadian предоставил решение в своем ответе, которое должно решить вашу проблему. Позвольте мне просто объяснить, что происходит.

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

Это ваше вычисляемое свойство:

var test: NSNumber {
    get { return self.test }
    set {
        println(newValue)
        self.test = newValue
    }
}

Посмотрите на реализацию геттера:

return self.test

Что оно делает? Он считывает свойство test текущего экземпляра и возвращает его. Что такое свойство test? Вот этот:

var test: NSNumber {
    get { return self.test }
    set {
        println(newValue)
        self.test = newValue
    }
}

Да, это одно и то же имущество. Что делает ваш геттер, так это рекурсивно и неопределенно долго вызывает себя, пока во время выполнения не произойдет сбой.

То же правило относится и к сеттеру:

self.test = newValue 

он продолжает вызывать себя, пока приложение не выйдет из строя.

person Antonio    schedule 03.07.2015
comment
Ясно, так что нет способа переопределить установщик в Swift, чтобы сделать что-то вроде set{self.test = newValue - 7} без другого сохраненного свойства? - person user3211122; 03.07.2015
comment
О, nvm, stackoverflow.com/questions/24006234/ был действительно полезен. Таким образом, set/get — это только методы, а didSet/willSet привязаны к ivars. - person user3211122; 03.07.2015

Переменные Swift по умолчанию являются синтезированными свойствами. В большинстве случаев этого достаточно (рекомендуется предпочесть типы Swift)

var test: Int

override init() {
    super.init()
    test = 5
}

Если вам нужно что-то сделать после установки переменной, используйте

var test: Int {
    didSet{
        println("\(oldValue) - \(newValue)")
    }
}

ваш код постоянно устанавливает переменную, вызывая установщик, который вызывает установщик, который …

person vadian    schedule 03.07.2015

Это бесконечный цикл; ваш сеттер рекурсивно вызывает себя.

var test: NSNumber {
    set {
        test = newValue
    }
}

Это компилируется нормально, и программист на Objective-C может ожидать отсутствие цикла из-за того, что вместо этого устанавливается «резервный ivar», такой как _test, а не повторный вызов метода установки.

Но переменная экземпляра _ivars, поддерживающая свойство, не существует в Swift для вычисляемых свойств, если только вы не создадите их самостоятельно.

person pkamb    schedule 11.06.2018