Mülk sahipleri ve ayarlayıcılar


195

Bu basit sınıfla derleyici uyarısı alıyorum

xKendi ayarlayıcı / alıcısında değişiklik yapmaya / erişmeye çalışmak

ve bu şekilde kullandığımda:

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

Bir EXC_BAD_ACCESS alıyorum. Bunu ivarları açıkça desteklemeden nasıl yapabilirim?

class Point {

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

1
Bunu yapmak derleyiciye fazladan yük getirir ve CPU'yu şiddetle tüketir. Ben sadece yaptım: | . Oluşturduğum hata buydu. (Oyun alanı kullanıyordum)
Bal

Kullanmak setistediğiniz kullanmak yerine bu davranış kafa karıştırıcıdır didSet. Özellikler, uyguladığınızda Swift'te Objective-C veya diğer dillerden farklı davranır set. Bkz @jack aşağıda cevabını ve örnek didSet@cSquirrel dan
Paul Solt

Yanıtlar:


232

Ayarlayıcılar ve Harfler computed properties; bu tür özelliklerin örnekte depolaması yoktur - alıcıdan alınan değerin diğer örnek özelliklerinden hesaplanması amaçlanır. Sizin durumunuzda xatanacak hiçbir şey yoktur .

Açıkça: "Bunu ivar'ları açıkça desteklemeden nasıl yapabilirim". Yapamazsınız - hesaplanan özelliği yedeklemek için bir şeye ihtiyacınız olacak. Bunu dene:

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

Özellikle, Swift REPL'de:

 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'teki seterler / alıcılar ObjC'den oldukça farklı. Özellik, hesaplanmış bir özellik haline gelir , bu da ObjC'de olduğu gibi bir destek değişkenine sahip olmadığı anlamına gelir _x.

Çözelti kodunda Gördüğünüz altında xTimesTwoyok değil bir şey saklamak, ama sadece hesaplar sonucunu x.

Hesaplanan mülklerle ilgili resmi belgeler bölümüne bakın .

İstediğiniz işlevler Özellik Gözlemciler de olabilir .

İhtiyacınız olan şey:

var x: Int

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

Setter / getters içindeki diğer özellikleri, amaçlandıkları şekilde değiştirebilirsiniz.


1
Evet, dokümantasyonda okuduğum şey bu. Mülk bölümünü atladım ve bunun bir yolu yok gibi görünüyor, değil mi?
Atomix

Evet, bunu gerçekten yapmak istiyorsanız, ilkini "geri" almak için başka bir örnek değişkenine ihtiyacınız olacaktır. Ancak, seterler bu amaç için tasarlanmamıştır, muhtemelen bununla ne yapmaya çalıştığınızı yeniden düşünmelisiniz.
Jack

5
didSetayarlandıktan hemen sonra değeri değiştirmenize izin veren kullanabilirsiniz . Olsa bile hiçbir şey için ...
ObjectiveCsam

1
Not: geçersiz giriş olduğu için değer dönmek isterseniz, set gerekiyordu xarkasına oldValueiçinde didSet. Bu davranış değişikliği Objective-C özelliklerinden gelen çok kafa karıştırıcı, teşekkürler!
Paul Solt

105

Özellik gözlemcisini kullanarak ayarlanan değeri özelleştirebilirsiniz. Bunu yapmak için 'set' yerine 'didSet' kullanın.

class Point {

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

Alıcıya gelince ...

class Point {

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

xBu modelde varsayılan bir değerle nasıl başlatabilirsiniz?
i_am_jorf

3
Öyle olacağını bkz var x: Int = 0 { didSet { ....
i_am_jorf

31

GoZoner'ın cevabını detaylandırmak için:

Buradaki asıl sorun, tekrarlayan kişinizi tekrar tekrar çağırmanızdır.

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
        }
    }

Kod yorumunun yukarıda önerdiği gibi, x özelliğinin alıcısını sonsuz bir şekilde çağırıyorsunuz, bu bir EXC_BAD_ACCESS kodu alana kadar çalışmaya devam edecek (Xcode'unuzun oyun ortamının sağ alt köşesinde dönücüyü görebilirsiniz).

Swift belgelerindeki örneği düşünün :

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

Merkezi hesaplanan özelliğin, değişkenin bildiriminde hiçbir zaman kendisini nasıl değiştirmediğine veya kendisini döndürmediğine dikkat edin.


Geçerli olan kural asla malını kendisi erişmek içinde alıcı yani get. Çünkü bu bir başkasını gettetikleyecek, bir başkasını tetikleyecekti. . . Yazdırmayın bile. Çünkü yazdırma işlemi, değeri yazdırmadan önce 'almayı' gerektirir!
Honey

19

Geçersiz kılmak setterve getterhızlı değişkenler için aşağıdaki kodu kullanın

var temX : Int? 
var x: Int?{

    set(newX){
       temX = newX
    }

    get{
        return temX
    }
}

Değişken değerini geçici bir değişken içinde tutmamız gerekir, çünkü alıcı / ayarlayıcı geçersiz kılınan aynı değişkene erişmeye çalışmak sonsuz döngülere neden olur.

Ayarlayıcıyı bu şekilde çağırabiliriz

x = 10

Getter, verilen kod satırının altına ateşlenerek çalıştırılacak

var newVar = x

8

Sen edilir yinelemeli tanımlayan xile x. Sanki biri kaç yaşında olduğunu soruyormuş gibi? "Ben iki yaşım" diye cevap veriyorsunuz. Bu anlamsız.

Sen iki kez John'un yaş veya başka bir değişken söylemek gerekir ama kendinizi.

hesaplanan değişkenler her zaman başka bir değişkene bağımlıdır.


Geçerli olan kural asla malını kendisi erişmek içinde alıcı yani get. Çünkü bu bir başkasını gettetikleyecek, bir başkasını tetikleyecekti. . . Yazdırmayın bile. Çünkü yazdırma işlemi, değeri yazdırmadan önce 'almayı' gerektirir!

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

let p1 = Person()

Bu şu uyarıyı verir:

'Name' sitesine kendi alıcısından erişmeye çalışmak.

Hata şu şekilde belirsiz görünüyor:

resim açıklamasını buraya girin

Alternatif olarak kullanmak isteyebilirsiniz didSet. İle didSetönceden ayarlanmış ve sadece ayarlanmış değeri tutun. Daha fazla bilgi için bu cevaba bakınız .


8

Güncelleme: Swift 4

Aşağıdaki sınıfta setter ve getter değişkene uygulanır 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
        }
   }

Nesne oluşturma

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

gaz giderici

print(triangle.perimeter) // invoking getter

setter

triangle.perimeter = 9.9 // invoking setter

6

Bunu kullanmayı deneyin:

var x:Int!

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

Bu temelde Jack Wu'nun cevabı, ama fark şu ki Jack Wu'nun cevabında x değişkeni var x: Intbenimkinde x değişkenim şöyle: var x: Int!yani yaptığım tek şey onu isteğe bağlı bir tip yapmaktı.


1

Swift 5.1 Güncellemesi

Swift 5.1'den itibaren, değişkeninizi get anahtar sözcüğü kullanmadan alabilirsiniz . Örneğin:

var helloWorld: String {
"Hello World"
}

Değiştirmeye çalışırsam, helloWorld = "macWorld" , değere atanamıyor: 'helloWorld' sadece bir get özelliği özelliği gösteriyor.
McDonal_11

yeni değerler atamak mümkün mü? ya da hayır ?
McDonal_11

Bunun mümkün olduğunu düşünmüyorum.
atalayasa

Yani edebi olarak, var helloWorld: String {"Hello World"} ve helloWorld: String = "Hello World" aynı olsun?
McDonal_11

Evet bencede.
atalayasa

0

Swift'teki ayarlayıcılar ve alıcılar hesaplanan özelliklere / değişkenlere uygulanır. Bu özellikler / değişkenler aslında bellekte depolanmaz, depolanan özelliklerin / değişkenlerin değerine göre hesaplanır.

Apple'ın konuyla ilgili Swift belgelerine bakın: Swift Değişken Bildirimleri .


0

İşte teorik bir cevap. Burada bulunabilir

{Get set} özelliği, sabit bir depolanmış özellik olamaz. Hesaplanmış bir özellik olmalı ve hem get hem de set uygulanmalıdır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.