Read-only and non-computed variable properties in Swift
我正在尝试用新的AppleSwift语言来解决问题。假设我以前在Objective-C中做过类似的事情,我有
我举个例子,一个非常简单的时钟。我会写在目标C中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | @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 |
为了一个特定的目的,我们不能直接触摸秒、分钟和小时,只允许使用一种方法一秒一秒地递增。只有此方法才能使用实例变量的技巧来更改值。
既然在斯威夫特没有这样的东西,我正试图找到一个等价物。如果我这样做:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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等价物没有意义。如果没有,如果有人有答案,我将非常感激;)
也许苹果承诺的未来访问控制机制就是答案?
谢谢!
只需在属性声明前面加上
1 2 3 | public private(set) var hours: UInt = 0 public private(set) var minutes: UInt = 0 public private(set) var seconds: UInt = 0 |
做你想做的事有两种基本的方法。第一种方法是拥有私有财产和返回该财产的公共计算财产:
1 2 3 4 5 6 7 8 9 | public class Clock { private var _hours = 0 public var hours: UInt { return _hours } } |
但是,这可以用一种不同的、较短的方式实现,如《Swift编程语言》一书的"访问控制"一节所述:
1 2 3 4 5 | public class Clock { public private(set) var hours = 0 } |
作为附带说明,在声明公共类型时必须提供公共初始化器。即使为所有属性提供默认值,
1 2 3 4 5 6 7 8 9 10 | public class Clock { public private(set) var hours = 0 public init() { hours = 0 } // or simply `public init() {}` } |
在讨论默认初始化程序时,本书的同一节也解释了这一点。
由于没有访问控制(也就是说,您不能根据呼叫者的身份制定不同的访问合同),下面是我现在要做的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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() } } |
这只是增加了一个间接的级别,就像以前那样,直接访问计数器;另一个类可以制作一个时钟,然后直接访问它的计数器。但是我们的想法——也就是说,我们试图达成的契约——是另一个类应该只使用时钟的顶级属性和方法。我们不能执行那个合同,但实际上在Objective-C中也很难执行。
实际上,访问控制(在Swift中还不存在)并不像您在目标C中想象的那样强制执行。如果人们真的想要,他们可以直接修改您的只读变量。它们只是不使用类的公共接口。
您可以在swift中执行类似的操作(剪切和粘贴您的代码,加上一些修改,我没有测试它):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | 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++ } } } } |
它与目标C中的相同,只是实际存储的属性在公共接口中可见。
在Swift中,您还可以做一些更有趣的事情,在目标C中也可以,但在Swift中可能更漂亮(在浏览器中编辑,我没有测试它):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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++ } } |