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

我正在尝试用新的 Apple Swift 语言解决一些问题。假设我曾经在 Objective-C 中做过类似于下面这样的事情。我有 readonly属性,它们不能单独更改。但是,使用特定的方法,可以按照逻辑方式更改属性。

我以下面这个例子为例,一个非常简单的时钟。

@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 等价物是没有意义的。如果没有,如果有人有答案,我将非常感激;)

也许苹果承诺的未来 访问控制机制就是答案?

谢谢!

64213 次浏览

实际上访问控制(在 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 中的属性相同。

在“迅速”中你还可以做一些更有趣的事情,在 Objective C 中也可以,但是在“迅速”中可能更漂亮(在浏览器中编辑过,我没有测试过) :

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

由于没有访问控制(这意味着你不能制定一个访问协议,不同的访问者是谁) ,这里是我现在要做的:

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 的顶级属性和方法。我们不能 执行那个合同,但实际上在 Objective-C 中也几乎不可能执行。

只需在属性声明前面加上 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设置为 private。

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

public class Clock {


private var _hours = 0


public var hours: UInt {
return _hours
}


}

但是,这可以通过一种不同的、更短的方式来实现,正如 “快速编程语言”书中的 “访问控制”部分所述:

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


}

在本书的同一部分,当谈到默认初始化程序时,也解释了这一点。