Власники нерухомості та сетери


195

За допомогою цього простого класу я отримую попередження про компілятор

Спроба змінити / отримати доступ xу межах власного сетера / геттера

і коли я використовую його так:

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

Я отримую EXC_BAD_ACCESS. Як я можу це зробити без явного резервного копіювання?

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; такі властивості не мають сховища в екземплярі - значення, отримане від getter, має бути обчислене з інших властивостей екземпляра. У вашому випадку не xможе бути призначено.

Явно: "Як я можу це зробити без явного підкріплення ivars". Ви не можете - вам знадобиться щось для резервного копіювання обчисленої властивості. Спробуйте це:

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

Зокрема, у Швидкій відповіді:

 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.

Дивіться офіційні документи про обчислені властивості .

Функція, яку ви хочете, також може бути спостерігачами власності .

Що вам потрібно:

var x: Int

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

Ви можете змінювати інші властивості в сеттері / getters, для чого вони призначені.


1
Так, це я читаю в документації. Я пропустив розділ власності, і, здається, немає способу обійти це, правда?
Атомікс

Так, вам знадобиться інша змінна інстанція, щоб "повернути" першу, якщо ви дійсно хочете це зробити. Однак сетери не призначені для цієї мети, ви, мабуть, повинні заново подумати, що ви намагаєтеся досягти з цим.
Джек

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

1
ПРИМІТКА. Якщо ви хочете повернути значення, оскільки воно було невірним, вам потрібно буде xповернутись oldValueу didSet. Ця зміна поведінки дуже заплутано виходить від властивостей Objective-C, дякую!
Пол Солт

105

Ви можете налаштувати встановлене значення за допомогою спостерігача властивості. Для цього використовуйте "didSet" замість "встановити".

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()

Оскільки це дало б таке попередження:

Спроба отримати доступ до "name" зсередини власного геттера.

Помилка виглядає приблизно так:

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

Як альтернативу ви можете використовувати 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 на тему: Швидкі декларації змінних .


0

Ось теоретична відповідь. Це можна знайти тут

Властивість {get set} не може бути постійною збереженою властивістю. Це має бути обчисленою властивістю, і отримати, і встановити, слід реалізувати.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.