Существует разница в поведении, когда переменная экземпляра вычисляется, а не хранится - PullRequest
2 голосов
/ 01 июня 2019

У меня есть пользовательский класс UIView, который инициируется из файла xib.Он имеет свойство экземпляра с именем title типа String?.Всякий раз, когда свойство title установлено, текст UITextField изменяется на значение свойства title.

Если свойство title является сохраненным свойством, программа работает какожидается.

Если свойство title является вычисляемым свойством, то происходит сбой программы с ошибкой EXC_BAD_ACCESS, которая, как я полагаю, связана с тем, что IBOutlet еще не был инициализирован.

МожетКто-нибудь объяснит, почему, если title является хранимым свойством, оно работает, но если это вычисляемое свойство, оно терпит неудачу?

Ниже приведен исходный код. NibView является подклассом UIView и обрабатываетзагрузка файла xib

class NibView: UIView {
    override init(frame: CGRect) {
        super.init(frame: frame)
        loadNib()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        loadNib()
    }
}

Реализация метода loadNib находится внутри расширения

extension UIView {
    func loadNib() {
        guard let view = nib.instantiate(withOwner: self, options: nil).first as? UIView else { return }

        view.frame = bounds
        addSubview(view)
    }
}

Определение свойства nib для UIView находится в другом расширении

extension UIView {
    static var nib: UINib {
        return UINib(nibName: String(describing: self), bundle: nil)
    }

    var nib: UINib {
        return type(of: self).nib
    }
}

Следующий класс является классом, обладающим свойством title.

class ProgressView: NibView {
    var title: String? {
        didSet {
            titleLabel.text = title
        }
    }

    @IBOutlet private weak var titleLabel: UILabel!
}

Приведенный выше класс используется следующим образом -

let view = ProgressView()
addSubview(view)
view.title = "Loading"

Запуск вышеуказанного кода работаеткак и ожидалось.Однако, если реализация ProgressView изменена для использования вычисляемого свойства, как показано ниже, то она завершится неудачно

class ProgressView: NibView {
    var title: String? {
        get {
            return titleLabel.text
        }
        set {
            titleLabel.text = newValue
        }
    }

    @IBOutlet private weak var titleLabel: UILabel!
}

Может кто-нибудь указать, почему существует разница в поведении, когда вместо свойства title вычисляетсясохраняются?

Редактировать - основной поток аварийно завершает работу с «Поток 1: EXC_BAD_ACCESS (code = EXC_I386_GPFLT)» *

Метод в верхней части стека вызовов - «ProgressView.title.modify».

Редактировать 2 - Я не уверен, что я сделал, но я не могу воспроизвести проблему после перезапуска xcode.Даже если используется вычисляемое свойство, оно работает как положено.

1 Ответ

2 голосов
/ 03 июня 2019

Ваше описание далеко не объяснительно, но я предполагаю, что есть перо ProgressView, в котором владельцем файла является ProgressView, и есть выход titleLabel от владельца файла к метке внутри кончика.(Я предполагаю это, потому что иначе я не могу объяснить, как вы используете withOwner: self.)

Исходя из этого предположения, я не могу воспроизвести любую проблему.Оба ваших способа выражения title прекрасно работают для меня.Я поместил print утверждений, чтобы убедиться, что вызывается правильный, и это так;Неважно, является ли это didSet или установщиком вычисляемого свойства, мы загружаемся очень хорошо, и я вижу текст "Загрузка".

Мой код находится в контроллере представления viewDidLoad, если это делаетразница.

(Кстати, я считаю, что вы используете ProgressView() с подозрением. Это приводит к представлению нулевого размера. Может показаться, что это не имеет никакого значения, но это плохая идея.метка является подпредставлением представления нулевого размера. Если представление нулевого размера обрезает свои подпредставления, метка будет невидимой. Даже если представление нулевого размера не обрезает свои подпредставления, если бы метка была кнопкой, кнопкане работает. Представления нулевого размера - плохая идея. Вы должны дать вашему ProgressView реальный кадр.)

...