#ios #swift
Вопрос:
Приведенный ниже код работает просто отлично
class A {
var s: MyStruct! {
didSet {
print("didSet")
print(n)
}
}
lazy var n: Int = s.x 1
func viewDidLoad() {
s = MyStruct()
}
}
struct MyStruct {
var x = 1
init() { print("MyStruct init") }
}
let a = A()
a.viewDidLoad()
с выходом :
MyStruct init
didSet
2
Однако, если у нас есть ленивые свойства, как показано ниже
class A {
var s: MyStruct! {
didSet {
print("didSet")
print(n)
}
}
lazy var n: Int = s.x 1
func viewDidLoad() {
s = MyStruct()
}
}
struct MyStruct {
lazy var x = 1
init() { print("MyStruct init") }
}
let a = A()
a.viewDidLoad()
Это закончится бесконечным рекурсивным вызовом
MyStruct init
didSet
didSet
didSet
...
Почему свойство lazy и didSet в конечном итоге вызовут рекурсию?
Комментарии:
1. ленивые вары на конструкциях следует использовать очень осторожно или вообще не использовать.
2. Доступ
x
к MyStruct-это операция изменения (mutating get
), поэтому это запускает свойствоdidSet
fors
. Таким образом, бесконечный циклprint(n)
-> получить >s.x
-> позвонить >s.didSet
-> >print(n)
-> …
Ответ №1:
Вы декларируете lazy stored
собственность. Когда структура инициализируется с MyStruct()
помощью , в ней не сохраняется значение MyStruct.x
.
Он будет заполнен только при первом доступе к нему. Когда свойство изменяется, тип значения, например struct MyStruct
, также считается измененным — поэтому оно didSet
вызывается (снова) при первом доступе x
.
Вот как это становится бесконечным циклом.
viewDidLoad() > A.s.setter > A.s.didset
[Ожидается]- Первый доступ
A.n.getter
к этойprint(n)
части. s.x
хранится лениво, и при первом заполнении значения (обновлении) оно срабатывает —A.s.modify > A.s.didset
и мы снова приземляемсяA.n.getter
.- Он бесконечно повторяется между
2
amp;3
после этого.
Комментарии:
1. Я все еще не понимаю. Когда мы приземлимся в третий раз
n.getter
, зачем мы достигнемs.modify
? В этот момент значениеn
должно быть уже известно (s.x
к тому времени должно быть инициализировано до 1). Разве он не может просто вернуть это, не получивs.x
?2. На скриншоте
Frame 11
не завершено выполнение — значениеn
НЕ было полученоprint(n)
частично, и каким-то образом нам удалось снова приземлиться в том же месте. Мы можем легко разорвать эту петлю, отложив вызовprint(n)
0.1 seconds
примерно на некоторое время. Пока он синхронен, он будет вести себя таким образом.