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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24081194/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 21:17:57  来源:igfitidea点击:

Read-only and non-computed variable properties in Swift

objective-cswiftproperties

提问by Zaxonov

I'm trying to figure out something with the new Apple Swift language. Let's say I used to do something like the following in Objective-C. I have readonlyproperties, and they cannot be individually changed. However, using a specific method, the properties are changed in a logical way.

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

I take the following example, a very simple clock. I would write this in Objective-C.

我以下面的例子为例,一个非常简单的时钟。我会用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

For a specific purpose, we cannot touch the seconds, minutes and hours directly, and it's only allowed to increment second by second using a method. Only this method could change the values by using the trick of the instance variables.

出于特定目的,我们不能直接触摸秒、分和小时,只能使用方法逐秒递增。只有这种方法才能通过使用实例变量的技巧来改变值。

Since there are no such things in Swift, I'm trying to find an equivalent. If I do this:

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

That would work, but anybody could change directly the properties.

那会起作用,但任何人都可以直接更改属性。

Maybe I already had a bad design in Objective-C and that's why the potential new Swift equivalent is not making sense. If not and if someone have an answer, I would be very grateful ;)

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

Maybe the future Access Control Mechanismspromised by Apple is the answer?

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

Thanks!

谢谢!

回答by Ethan

Simply prefix the property declaration with private(set), like so:

只需在属性声明前加上private(set),就像这样:

public private(set) var hours:   UInt = 0
public private(set) var minutes: UInt = 0
public private(set) var seconds: UInt = 0

privatekeeps it local to a source file, while internalkeeps it local to the module/project.

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

private(set)creates a read-onlyproperty, while privatesets both, setand getto private.

private(set)创建一个read-only属性,而private套两,setget私人。

回答by elitalon

There are two basic ways of doing what you want. First way is by having a private property and and a public computed property which returns that property:

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

public class Clock {

  private var _hours = 0

  public var hours: UInt {
    return _hours
  }

}

But this can be achieved in a different, shorter way, as stated in the section "Access Control"of the "The Swift Programming Language"book:

但这可以通过不同的、更短的方式实现,如“The Swift Programming Language”一书的“Access Control”部分所述:

public class Clock {

    public private(set) var hours = 0

}

As a side note, you MUST provide a public initialiser when declaring a public type. Even if you provide default values to all properties, init()must be defined explicitly public:

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

public class Clock {

    public private(set) var hours = 0

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

}

This is also explained in the same section of the book, when talking about default initialisers.

这在本书的同一部分中也有解释,在讨论默认初始化程序时。

回答by matt

Since there are no access controls (meaning that you can't make an access contract that differs depending on who the caller is), here's what I would do for now:

由于没有访问控制(意味着您不能根据调用者的身份制定不同的访问合同),因此我现在要做的是:

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

This merely adds a level of indirection, as it were, to direct access to the counter; another class canmake a Clock and then access its counter directly. But the idea— i.e., the contract we're trying to make — is that another class should use the Clock's top-level properties and methods only. We can't enforcethat contract, but actually it was pretty much impossible to enforce in Objective-C too.

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

回答by Analog File

Actually access control (which does not exist yet in Swift) is not as enforced as you may think in Objective C. People canmodify your readonly variables directly, if they really want to. They just do not do it with the public interface of the class.

实际上,访问控制(在 Swift 中尚不存在)并不像您在 Objective C 中想象的那样强制执行。人们可以直接修改您的只读变量,如果他们真的想要的话。他们只是不使用类的公共接口。

You can do something similar in Swift (cut&paste of your code, plus some modifications, I did not test it):

你可以在 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++
            }
        }
    }
}

which is the same as what you have in Objective C except that the actual stored properties are visible in the public interface.

除了实际存储的属性在公共接口中可见之外,这与您在 Objective C 中所拥有的相同。

In swift you can also do something more interesting, which you can also do in Objective C, but it's probably prettier in swift (edited in the browser, I did not test it):

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