Собиратели и установщики недвижимости


195

С этим простым классом я получаю предупреждение компилятора

Попытка изменить / получить доступ в xпределах своего собственного установщика / получателя

и когда я использую это так:

var p: point = Point()
p.x = 12

Я получаю EXC_BAD_ACCESS. Как я могу сделать это без явной поддержки ivars?

class Point {

    var x: Int {
        set {
            x = newValue * 2 //Error
        }
        get {
            return x / 2 //Error
        }
    }
    // ...
}

1
Это также увеличит нагрузку на компилятор и будет сильно загружать процессор. Я просто сделал это: | , Это была ошибка, которую я создавал. (Я использовал детскую площадку)
Мед

Это поведение сбивает с толку, вместо использования setвы хотите использовать didSet. При реализации свойства ведут себя по-разному в Swift, чем Objective-C или другие языки set. Смотрите ответ ниже от @jack и пример didSetот @cSquirrel
Пол Солт

Ответы:


232

Сеттеры и геттеры применяются к computed properties; такие свойства не имеют хранилища в экземпляре - значение из получателя предназначено для вычисления из других свойств экземпляра. В вашем случае нет xназначения.

Явно: «Как я могу сделать это без явной поддержки ivars». Вы не можете - вам понадобится что-то для резервного копирования вычисленного свойства. Попробуй это:

class Point {
  private var _x: Int = 0             // _x -> backingX
  var x: Int {
    set { _x = 2 * newValue }
    get { return _x / 2 }
  }
}

В частности, в Swift REPL:

 15> var pt = Point()
pt: Point = {
  _x = 0
}
 16> pt.x = 10
 17> pt
$R3: Point = {
  _x = 20
}
 18> pt.x
$R4: Int = 10

106

Сеттеры / геттеры в Swift сильно отличаются от ObjC. Свойство становится вычисляемым свойством, что означает, что оно не имеет _xвспомогательной переменной, как в ObjC.

В приведенном ниже коде решения вы можете видеть, что xTimesTwoничего не хранится, а просто вычисляется результат x.

Смотрите Официальные документы по вычислительным свойствам .

Функциональность, которую вы хотите, также может быть Property Observers .

Что вам нужно это:

var x: Int

var xTimesTwo: Int {
    set {
       x = newValue / 2
    }
    get {
        return x * 2
    }
}

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


1
Да, это то, что я читаю в документации. Я пропустил раздел свойств, и кажется, что нет никакого способа обойти это, верно?
Atomix

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

5
Вы можете использовать, didSetчто позволит вам изменить значение сразу после его установки. Ничего не получится, хотя ...
ObjectiveCsam

1
ПРИМЕЧАНИЕ. Если вы хотите отменить значение, поскольку оно было введено неверно, вам нужно будет xвернуться к нему oldValueв didSet. Это изменение в поведении очень запутанно, исходя из свойств Objective-C, спасибо!
Пол Солт

105

Вы можете настроить установленное значение с помощью свойства обозревателя. Для этого используйте «didSet» вместо «set».

class Point {

var x: Int {
    didSet {
        x = x * 2
    }
}
...

Что касается добытчика ...

class Point {

var doubleX: Int {
    get {
        return x / 2
    }
}
...

Как вы могли бы инициализировать xсо значением по умолчанию в этом шаблоне?
i_am_jorf

3
Я вижу, что это будет: var x: Int = 0 { didSet { ....
i_am_jorf

31

Чтобы уточнить ответ GoZoner:

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

var x:Int
    {
        set
        {
            x = newValue * 2 // This isn't a problem
        }
        get {
            return x / 2 // Here is your real issue, you are recursively calling 
                         // your x property's getter
        }
    }

Как и в приведенном выше комментарии к коду, вы бесконечно вызываете метод получения свойства x, который будет выполняться до тех пор, пока вы не получите код EXC_BAD_ACCESS (вы можете увидеть счетчик в правом нижнем углу игровой среды вашего Xcode).

Рассмотрим пример из документации Swift :

struct Point {
    var x = 0.0, y = 0.0
}
struct Size {
    var width = 0.0, height = 0.0
}
struct AlternativeRect {
    var origin = Point()
    var size = Size()
    var center: Point {
        get {
            let centerX = origin.x + (size.width / 2)
            let centerY = origin.y + (size.height / 2)
            return Point(x: centerX, y: centerY)
        }
        set {
            origin.x = newValue.x - (size.width / 2)
            origin.y = newValue.y - (size.height / 2)
        }
    }
}

Обратите внимание, что вычисляемое в центре свойство никогда не изменяет и не возвращает себя в объявлении переменной.


Правило большого пальца никогда не получить доступ к самой собственности из внутри - газопоглотителя т.е. get. Потому что это вызовет другое, getкоторое вызовет другое. , , Даже не печатайте это. Потому что для печати также необходимо «получить» значение, прежде чем оно сможет его напечатать!
Мед

19

Для переопределения setterи getterдля быстрых переменных используйте приведенный ниже код

var temX : Int? 
var x: Int?{

    set(newX){
       temX = newX
    }

    get{
        return temX
    }
}

Нам нужно сохранить значение переменной во временной переменной, поскольку попытка доступа к той же переменной, чей метод getter / setter переопределяется, приведет к бесконечным циклам.

Мы можем вызвать сеттер просто так

x = 10

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

var newVar = x

8

Вы рекурсивно определяете xс x. Как будто кто-то спрашивает тебя, сколько тебе лет? И вы отвечаете: «Я вдвое старше меня». Что бессмысленно.

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

вычисляемые переменные всегда зависят от другой переменной.


Правило большого пальца никогда не получить доступ к самой собственности из внутри - газопоглотителя т.е. get. Потому что это вызовет другое, getкоторое вызовет другое. , , Даже не печатайте это. Потому что для печати также необходимо «получить» значение, прежде чем оно сможет его напечатать!

struct Person{
    var name: String{
        get{
            print(name) // DON'T do this!!!!
            return "as"
        }
        set{
        }
    }
}

let p1 = Person()

Так как это даст следующее предупреждение:

Попытка получить доступ к «имени» из своего собственного геттера.

Ошибка выглядит примерно так:

введите описание изображения здесь

В качестве альтернативы вы можете использовать didSet. С помощью didSetвы получите значение, которое было установлено ранее и только что установлено. Для получения дополнительной информации см. Этот ответ .


8

Обновление: Swift 4

В приведенном ниже классе сеттер и геттер применяются к переменной sideLength

class Triangle: {
    var sideLength: Double = 0.0

    init(sideLength: Double, name: String) { //initializer method
        self.sideLength = sideLength
        super.init(name: name)
        numberOfSides = 3
    }

    var perimeter: Double {
        get { // getter
            return 3.0 * sideLength
        }
        set(newValue) { //setter
            sideLength = newValue / 4.0
        }
   }

Создание объекта

var triangle = Triangle(sideLength: 3.9, name: "a triangle")

добытчик

print(triangle.perimeter) // invoking getter

сеттер

triangle.perimeter = 9.9 // invoking setter

6

Попробуйте использовать это:

var x:Int!

var xTimesTwo:Int {
    get {
        return x * 2
    }
    set {
        x = newValue / 2
    }
}

Это в основном ответ Джека Ву, но разница состоит в том, что в ответе Джека Ву его переменная x var x: Int, по моему, моя переменная x, выглядит следующим образом: var x: Int!поэтому я всего лишь сделал ее необязательным типом.


1

Обновление для Swift 5.1

Начиная с Swift 5.1, теперь вы можете получить свою переменную без использования ключевого слова get . Например:

var helloWorld: String {
"Hello World"
}

Если я попытаюсь изменить значение helloWorld = "macWorld" , я не смогу присвоить значение: helloWorld - это свойство, доступное только для получения, которое показывает ошибку.
McDonal_11

Можно ли назначить новые значения? или нет?
McDonal_11

Я не думаю, что это возможно.
atalayasa

Таким образом, var helloWorld: String {"Hello World"} и пусть helloWorld: String = "Hello World" одинаковы?
McDonal_11,

Да, я так думаю.
atalayasa

0

Сеттеры и геттеры в Swift применяются к вычисляемым свойствам / переменным. Эти свойства / переменные на самом деле не хранятся в памяти, а вычисляются на основе значения хранимых свойств / переменных.

См. Документацию Apple Swift на эту тему: Объявления переменных Swift .


0

Вот теоретический ответ. Это можно найти здесь

Свойство {get set} не может быть постоянным хранимым свойством. Это должно быть вычисляемое свойство, и get и set должны быть реализованы.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.