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

Zax*_*nov 121 properties objective-c swift

我正在尝试使用新的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
Run Code Online (Sandbox Code Playgroud)

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

由于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++
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这可行,但任何人都可以直接改变属性.

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

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

谢谢!

Eth*_*han 348

只需在属性声明前加上前缀private(set),如下所示:

public private(set) var hours:   UInt = 0
public private(set) var minutes: UInt = 0
public private(set) var seconds: UInt = 0
Run Code Online (Sandbox Code Playgroud)

private将其保存在源文件的本地,同时internal将其保留在模块/项目的本地.

private(set)创建一个read-only属性,同时private设置属性setget私有属性.

  • "private"将其保存在源文件的本地,"internal"将其保留在模块/项目的本地.private(set)仅将set设置为private.私有使set和get函数都是私有的 (28认同)
  • 第一条评论应添加到此答案中,以使其更完整. (3认同)

eli*_*lon 20

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

public class Clock {

  private var _hours = 0

  public var hours: UInt {
    return _hours
  }

}
Run Code Online (Sandbox Code Playgroud)

但这可以用不同的,更短的方式实现,如"Swift编程语言"一书的"访问控制"一节所述:

public class Clock {

    public private(set) var hours = 0

}
Run Code Online (Sandbox Code Playgroud)

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

public class Clock {

    public private(set) var hours = 0

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

}
Run Code Online (Sandbox Code Playgroud)

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


mat*_*att 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() }
}
Run Code Online (Sandbox Code Playgroud)

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


Ana*_*ile 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++
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这与 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++
    }
}
Run Code Online (Sandbox Code Playgroud)