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


126

我正在尝试使用新的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替代物没有意义的原因。如果没有,如果有人回答,我将不胜感激;)

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

谢谢!


2
访问控制就是答案。您将创建只读的计算属性,而将私有属性用于实际数据。
Leo Natan 2014年

确保打开错误报告(增强功能),以使Apple知道这种情况的严重程度。
Leo Natan 2014年

1
对于新用户:滚动到底部……
古斯塔夫·罗森布拉德

2
请标记@Ethan的答案正确。
phatmann

Answers:


356

只需在属性声明的前面加上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私有属性。


28
“专用”将其保留在源文件本地,“内部”将其保留在模块/项目本地。private(set)仅将集合设为私有。私有将设置和获取功能都私有化
user965972

3
应该在此答案中添加第一个注释,以使其更加完整。
罗布·诺贝克

现在(Swift 3.0.1)的访问控制级别已更改:“ fileprivate”声明只能由与声明相同的源文件中的代码访问”。“私有”声明只能由声明的直接包围范围内的代码访问。
侏罗纪'17

20

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

public class Clock {

  private var _hours = 0

  public var hours: UInt {
    return _hours
  }

}

但这可以通过另一种更短的方式来实现,如“ Swift编程语言”书的“访问控制”部分所述:

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

}

在谈论默认初始化程序时,在本书的同一部分中也对此进行了说明。


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中也几乎不可能执行。


从那以后看起来好像已经改变了,对吗? developer.apple.com/swift/blog/?id=5
Dan Rosenstark

1
@肯定,这个问题/答案在将访问控制添加到Swift之前就已经写好了。而且Swift 仍在不断发展,因此答案将继续过时。
马特2015年

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

“人们可以直接修改您的只读变量”-您到底是什么意思?怎么样?
user1244109

我指的是ObjectiveC。在Objective C中,您可以通过Objective C运行时API动态访问类的所有内容。
Analog File

我以某种方式理解您暗示这一点。很好奇。谢谢
user1244109
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.