Swift'deki salt okunur ve hesaplanmayan değişken özellikleri


126

Yeni Apple Swift diliyle bir şeyler çözmeye çalışıyorum. Diyelim ki Objective-C'de aşağıdaki gibi bir şey yapıyordum. readonlyMülklerim var ve tek tek değiştirilemezler. Ancak, belirli bir yöntem kullanılarak, özellikler mantıksal bir şekilde değiştirilir.

Şu örneği alıyorum, çok basit bir saat. Bunu Objective-C'de yazardım.

@interface Clock : NSObject

@property (readonly) NSUInteger hours;
@property (readonly) NSUInteger minutes;
@property (readonly) NSUInteger seconds;

- (void)incrementSeconds;

@end

@implementation Clock

- (void)incrementSeconds {
     _seconds++;

     if (_seconds == 60) {
        _seconds = 0;
        _minutes++;

         if (_minutes == 60) {
            _minutes = 0;
            _hours++;
        }
    }
}

@end

Belirli bir amaç için, saniyelere, dakikalara ve saatlere doğrudan dokunamayız ve bir yöntem kullanarak yalnızca saniye saniye artmasına izin verilir. Yalnızca bu yöntem, örnek değişkenlerin hilesini kullanarak değerleri değiştirebilir.

Swift'de böyle bir şey olmadığı için, bir eşdeğer bulmaya çalışıyorum. Bunu yaparsam:

class Clock : NSObject {

    var hours:   UInt = 0
    var minutes: UInt = 0
    var seconds: UInt = 0

    func incrementSeconds() {

        self.seconds++

        if self.seconds == 60 {

            self.seconds = 0
            self.minutes++

            if self.minutes == 60 {

                self.minutes = 0
                self.hours++
            }
        }
    }
}

Bu işe yarar, ancak herhangi biri doğrudan özellikleri değiştirebilir.

Belki zaten Objective-C'de kötü bir tasarımım vardı ve bu yüzden potansiyel yeni Swift eşdeğeri mantıklı değil. Olmazsa ve birinin cevabı olursa çok minnettar olurum;)

Belki cevap, Apple'ın vaat ettiği gelecekteki Erişim Kontrol Mekanizmalarıdır ?

Teşekkürler!


2
Cevap, erişim kontrolüdür. Gerçek veriler için özel mülkler kullanırken salt okunur olarak hesaplanan mülkler oluşturursunuz.
Leo Natan

Apple'a bunun ne kadar eksik olduğunu bildirmek için bir hata raporu (geliştirme) açtığınızdan emin olun.
Leo Natan

1
Yeni kullanıcılar için: En alta kaydırın ...
Gustaf Rosenblad

2
Lütfen @ Ethan'ın yanıtını doğru olarak işaretleyin.
phatmann

Yanıtlar:


356

Mülk bildiriminin önüne koymanız yeterlidir. private(set) :

public private(set) var hours:   UInt = 0
public private(set) var minutes: UInt = 0
public private(set) var seconds: UInt = 0

private bir kaynak dosyada yerel olarak tutar, internalmodül / proje için yerel , onu yerel tutar.

private(set)Bir yaratır read-onlyiken, mülk privatesetleri hem setve getgizli olarak.


28
Bir kaynak dosyada yerel olarak tutmak için "özel", modül / proje için yerel tutmak için "dahili". private (set) yalnızca seti özel yapar. Özel, hem ayarlama hem de alma işlevlerini özel yapar
user965972

3
Daha eksiksiz hale getirmek için bu cevaba ilk yorum eklenmelidir.
Rob Norback

Şu anda (Swift 3.0.1) Erişim Kontrol Seviyeleri değişti: "fileprivate" bildirimine yalnızca bildirimle aynı kaynak dosyadaki kodla erişilebilir ”. "özel" bildirime yalnızca bildirimin yakın çevreleyen kapsamı içindeki kodla erişilebilir.
Jurasic

20

İstediğinizi yapmanın iki temel yolu vardır. İlk yol, özel bir mülke ve bu mülkü döndüren herkese açık bir hesaplanmış mülke sahip olmaktır:

public class Clock {

  private var _hours = 0

  public var hours: UInt {
    return _hours
  }

}

Ancak bu , "Swift Programlama Dili" kitabının "Erişim Kontrolü" bölümünde belirtildiği gibi, farklı ve daha kısa bir yolla gerçekleştirilebilir :

public class Clock {

    public private(set) var hours = 0

}

Bir yan not olarak, genel bir tür bildirirken bir genel başlatıcı sağlamalısınız. Tüm özelliklere varsayılan değerler sağlasanız bile, init()açıkça genel olarak tanımlanmalıdır:

public class Clock {

    public private(set) var hours = 0

    public init() {
      hours = 0
    }
    // or simply `public init() {}`

}

Bu, kitabın aynı bölümünde, varsayılan başlatıcılardan bahsederken açıklanmaktadır.


5

Erişim denetimi olmadığından (arayanın kim olduğuna bağlı olarak değişen bir erişim sözleşmesi yapamayacağınız anlamına gelir), şimdilik yapacağım şey şu:

class Clock {
    struct Counter {
        var hours = 0;
        var minutes = 0;
        var seconds = 0;
        mutating func inc () {
            if ++seconds >= 60 {
                seconds = 0
                if ++minutes >= 60 {
                    minutes = 0
                    ++hours
                }
            }
        }
    }
    var counter = Counter()
    var hours : Int { return counter.hours }
    var minutes : Int { return counter.minutes }
    var seconds : Int { return counter.seconds }
    func incrementTime () { self.counter.inc() }
}

Bu sadece, sayaca doğrudan erişim için olduğu gibi bir dolaylılık düzeyi ekler; Başka sınıf edebilir bir Saat yapıp sonra doğrudan sayacı erişin. Ancak fikir - yani, yapmaya çalıştığımız sözleşme - başka bir sınıfın yalnızca Clock'ın üst düzey özelliklerini ve yöntemlerini kullanması gerektiğidir. Bu sözleşmeyi uygulayamayız , ancak aslında Objective-C'de de uygulamak neredeyse imkansızdı.


Görünüşe göre bu o zamandan beri değişmiş, değil mi? developer.apple.com/swift/blog/?id=5
Dan Rosenstark

1
@Evet, tüm bu soru / cevap, Swift'e erişim kontrolü eklenmeden çok önce yazılmıştı . Ve Swift hala gelişiyor, bu yüzden cevaplar güncelliğini yitirmeye devam edecek.
mat

2

(Swift henüz yok) Aslında erişim kontrolü gibi Amaç C. People sandığı gibi zorlanmaz can gerçekten istiyorsanız, doğrudan salt okunur değişkenlerini değiştirmek. Bunu sınıfın genel arayüzüyle yapmazlar.

Swift'de benzer bir şey yapabilirsiniz (kodunuzu kesip yapıştırın, artı bazı değişiklikler, ben test etmedim):

class Clock : NSObject {

    var _hours:   UInt = 0
    var _minutes: UInt = 0
    var _seconds: UInt = 0

    var hours: UInt {
    get {
      return _hours
    }
    }

    var minutes: UInt {
    get {
      return _minutes
    }
    }

    var seconds: UInt  {
    get {
      return _seconds
    }
    }

    func incrementSeconds() {

        self._seconds++

        if self._seconds == 60 {

            self._seconds = 0
            self._minutes++

            if self._minutes == 60 {

                self._minutes = 0
                self._hours++
            }
        }
    }
}

bu, gerçek depolanan özelliklerin genel arayüzde görünür olması dışında, Hedef C'de sahip olduğunuzla aynıdır.

Swift ile daha ilginç bir şey de yapabilirsiniz, ki bunu Objective C'de de yapabilirsiniz, ancak muhtemelen swift'te daha güzeldir (tarayıcıda düzenlendi, ben test etmedim):

class Clock : NSObject {

    var hours: UInt = 0

    var minutes: UInt {
    didSet {
      hours += minutes / 60
      minutes %= 60
    }
    }

    var seconds: UInt  {
    didSet {
      minutes += seconds / 60
      seconds %= 60
    }
    }

    // you do not really need this any more
    func incrementSeconds() {
        seconds++
    }
}

"İnsanlar salt okunur değişkenlerinizi doğrudan değiştirebilir" - tam olarak ne demek istiyorsunuz? Nasıl?
user1244109

Objective C'den bahsediyordum. Objective C'de, Objective C Runtime API'leri aracılığıyla bir sınıfla ilgili her şeye dinamik erişime sahipsiniz.
Analog Dosya

Bunu hızlı bir şekilde ima ettiğini bir şekilde anladım. Merak ettim. Teşekkürler
user1244109
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.