2017-02-09 2 views
4

Хорошо известно, что, разумеется, didSet не будет запускаться на одном и том же объекте изнутри didSet. (example.)Внутренняя защита didSet причудливо распространяется на весь класс?

Однако. Кажется, что: ограничение распространяется не только на этот объект, но, возможно, на любой объект того же класса.

Здесь приведены примеры для копирования-вставки для игровой площадки.

class C { 
    var Test: Bool = false { 
     didSet { 
      print("test.") 
      for c in r { 
       c.Test = true 
      } 
     } 
    } 
    var r:[C] = [] 
} 
var a:C = C() 
var b:C = C() 
var c:C = C() 
a.r = [b, c] 
a.Test = false 

Не работает!

class C { 
    var Test2: Bool = false { 
     didSet { 
      print("test2.") 
      global.Test2 = true 
     } 
    } 
} 
var global:C = C() 
var a:C = C() 
a.Test2 = false 

Не работает!

  1. Является ли это быстрой ошибкой?

  2. Если нет, что такое фактическое ограничение? Он не будет запускать ANY didSet (что бы то ни было), который начинается с didSet ?; один и тот же класс ?; тот же суперкласс ?; или?

  3. Где именно это объясняется в doco?

WTF. Нужно знать ... каково фактическое ограничение конкретно?

+0

Теоретически один из детей может быть сам собой. что означает бесконечный цикл. Может быть, ошибка, может быть особенность. Это не ответ, поэтому причина в комментариях :) – PeejWeej

+1

@JoeBlow: этот комментарий в исходном коде Swift (https://github.com/apple/swift/blob /master/lib/AST/Decl.cpp#L1072) может быть релевантным: * «Наблюдающий элемент доступен непосредственно из своих спецификаторов didSet/willSet, что предотвращает контировки от бесконечных циклов». * Вы также можете попытаться задать список рассылки быстрых пользователей, где люди из команды Swift регулярно вносят свой вклад. –

+0

Это похоже на тот же вопрос: [Почему нет бесконечной петли в didSet?] (Http://stackoverflow.com/questions/29363170/why-no-infinite-loop-in-didset). –

ответ

1

Это ошибка SR-419.

Из комментария на ошибку:

Тьфу. Нам действительно нужно проверить, что база доступа к свойствам является статически self.

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

class A { 
    var name: String 
    var related: A? 
    var property1: Int = 0 { 
     didSet { 
      print("\(name), setting property 1: \(property1)") 

      self.property2 = 100 * property1 
      related?.property1 = 10 * property1 
      related?.property2 = 100 * property1 
     } 
    } 
    var property2: Int = 0 { 
     didSet { 
      print("\(name), setting property 2: \(property2)") 
     } 
    } 

    init(name: String) { 
     self.name = name 
    } 
} 

let a = A(name: "Base") 
a.related = A(name: "Related") 
a.property1 = 2 

Выход:

Основание, установив свойство 1: 2
Основание, установив свойство 2: 200
Связанные, установив свойство 2: 200

когда ожидаемый выход должен быть:

Основание, установив свойство 1: 2
Основание, установив свойство 2: 200
по теме, присвоив свойству 1: 20
Связанные, установив свойство 2: 2000
относящиеся, установив свойство 2: 200

Кажется, вам также необходимо назначить это свойство непосредственно от наблюдателя. После того, как вы вводите другую функцию (или наблюдателя), наблюдатели начинают работать снова:

var property1: Int = 0 { 
    didSet { 
     print("\(name), setting property 1: \(property1)") 

     onSet() 
    } 
} 

...  
func onSet() { 
    self.property2 = 100 * property1 
    related?.property1 = 10 * property1 
    related?.property2 = 100 * property1 
} 

И это лучший обходной путь.

Другой обходной путь (спасибо @Hamish), чтобы обернуть вложенные задания в немедленно выполненного закрытия:

var property1: Int = 0 { 
    didSet { 
     { 
      self.property2 = 100 * property1 
      related?.property1 = 10 * property1 
      related?.property2 = 100 * property1 
     }() 
    } 
} 

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

+0

@JoeBlow Из моего тестирования он работает правильно для несвязанных свойств. Кроме того, если вы вызываете 'property1.didSet' из' property2.didSet', который вызывается из 'property1.didSet', тогда вы получаете хороший бесконечный цикл, как ожидалось. Я не тестировал подклассы, но я думаю, что он будет работать одинаково - это все тот же объект. – Sulthan

+1

Еще одно интересное обходное решение - использование немедленно выполненного закрытия: '_ = {self.related? .property1 = 10 * self.property1}()' (по какой-то причине Swift не нравится, если вы не говорите ' _ = '). – Hamish

+0

@Hamish вам нужно добавить ручной возврат, чтобы вернуть его void :) – Sulthan

Смежные вопросы