我试图用新的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承诺未来的访问控制机制可能就是答案吗?
谢谢!
答案 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
会将set
和get
设置为私有。
答案 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++
}
}