Swift中的只读和非计算变量属性

时间:2014-06-06 11:51:09

标签: objective-c swift properties

我试图用新的Apple Swift语言弄清楚什么。让我们说我曾经在Objective-C中做过类似的事情。我有readonly个属性,无法单独更改。但是,使用特定方法,属性会以逻辑方式更改。

我采用以下示例,一个非常简单的时钟。我会在Objective-C中写这个。

@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

出于特定目的,我们无法直接触摸秒,分钟和小时,并且只允许使用方法逐秒递增。只有这种方法可以通过使用实例变量的技巧来改变值。

由于Swift中没有这样的东西,我试图找到一个等价物。如果我这样做:

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

这样可行,但任何人都可以直接更改属性。

也许我已经在Objective-C中设计了一个糟糕的设计,这就是为什么潜在的新Swift等价物没有意义。如果没有,如果有人有答案,我将非常感激;)

Apple承诺未来的访问控制机制可能就是答案吗?

谢谢!

4 个答案:

答案 0 :(得分:340)

简单地在属性声明前加上private(set),如下:

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

private将其保留在源文件的本地,而internal将其保留在模块/项目的本地。

private(set)会创建read-only属性,而private会将setget设置为私有。

答案 1 :(得分:20)

有两种基本的方法可以做你想做的事。第一种方法是拥有一个私有属性和一个返回该属性的公共计算属性:

public class Clock {

  private var _hours = 0

  public var hours: UInt {
    return _hours
  }

}

但是,如"Access Control"书的"The Swift Programming Language"部分所述,这可以通过不同的更短的方式实现:

public class Clock {

    public private(set) var hours = 0

}

作为旁注,您必须在声明公共类型时提供公共初始化程序。即使您为所有属性提供默认值,也必须明确定义init() public:

public class Clock {

    public private(set) var hours = 0

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

}

在讨论默认初始化程序时,本书的相同部分也对此进行了解释。

答案 2 :(得分:5)

由于没有访问控制(意味着您无法根据呼叫者的身份制定不同的访问合同),以下是我现在要做的事情:

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

这仅仅增加了直接访问计数器的间接程度;另一个类可以创建一个Clock然后直接访问它的计数器。但的想法 - 即我们试图达成的合同 - 是另一个类应该只使用Clock的顶级属性和方法。我们不能强制执行该合同,但实际上在Objective-C中实施也几乎是不可能的。

答案 3 :(得分:2)

实际上,访问控制(在Swift中尚不存在)并不像您在Objective C中想象的那样强制执行。如果他们真的想要,那么可以直接修改您的只读变量。他们只是不使用该类的公共接口。

你可以在Swift中做类似的事情(剪切和粘贴你的代码,加上一些修改,我没有测试它):

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

与Objective C中的相同,只是实际存储的属性在公共接口中可见。

在swift中你也可以做一些更有趣的事情,你也可以在Objective C中做,但它在swift中可能更漂亮(在浏览器中编辑,我没有测试它):

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