Swift 中 属性 实例的只读访问权限
Read-only access for instance property in Swift
我知道private(set)
如讨论的那样here and here,但它似乎不适用于我拥有具有内部属性的实例变量的情况。
我有一个实例 属性,我希望它具有只读访问权限。以下代码显示了一些具体示例。
class Point {
var x: Int
var y: Int
init(_ x: Int, _ y: Int) {
self.x = x
self.y = y
}
}
class Sprite {
private(set) var origin = Point(0,0)
func incrementX() {
origin.x += 1
}
func incrementY() {
origin.y += 1
}
}
在这里,我的意图是让Sprite
成为origin
的唯一所有者,允许它是唯一可以修改它的人,否则让origin
只读到public。然而:
var sprite = Sprite()
sprite.origin = Point(100, 200) // Error: setter is inaccessibie. Expected behavior.
sprite.origin.x = 150 // No error. The origin.x will be set to 150.
显然我仍然可以通过直接修改内部 x
和 y
来修改 origin
,但这不是我的意图。
如何使 origin
真正只读?我错过了什么吗?
将 Point
修改为 private var x: Int
或 fileprivate var x: Int
对我不起作用,因为我希望能够让外部 class 像 Sprite
修改 Point
.
编辑:从下面的评论来看,这不是我的实际情况,因为有人评论说我应该使用 CGPoint
。该代码作为示例使我的问题更加具体。我实际上在另一个 class 中有一个复杂的 class 作为实例 属性,而不是这个简单的 Point
。由于其他设计限制,我也不能将其用作 struct
。
Edit2:下面的回答指出您不能阻止引用可变。这是我刚刚学到的 Swift 的一个限制,因为显然,我可以使用 Point const &
:
在 C++ 中实现我想要的
class Point {
public:
int x;
int y;
Point(int _x, int _y) {
x = _x;
y = _y;
}
Point & operator=(const Point & rhs) {
x = rhs.x;
y = rhs.y;
return *this;
}
...
};
class Sprite {
Point origin;
public:
Sprite()
: origin(0,0)
{}
// HERE
Point const & getOrigin() const { return origin; }
void incrementX() { origin.x += 1; }
void incrementY() { origin.y += 1; }
};
int main(int argc, const char * argv[]) {
Sprite sprite = Sprite();
Point const & o = sprite.getOrigin();
cout << "o: " << o << endl; // (0,0)
// o.x = 10; // Error: Cannot assign to const-qualified type.
// o = Point (100, 200); // Error: no viable overlaod =.
sprite.incrementX();
cout << "o: " << o << endl; // (1,0). Swift can't get this behavior with `struct Point`, but it won't prevent some writing attempts with `class Point`.
cout << "sprite.origin: " << sprite.getOrigin() << endl; // (1,0).
return 0;
}
只需将 Point
设为结构体
struct Point {
var x: Int
var y: Int
init(_ x: Int, _ y: Int) {
self.x = x
self.y = y
}
}
class Sprite {
private(set) var origin = Point(0,0)
func incrementX() {
origin.x += 1
}
func incrementY() {
origin.y += 1
}
}
然后重试
var sprite = Sprite()
sprite.origin.x = 1
error: cannot assign to property: 'origin' setter is inaccessible
BTW: Why don't you simply use CGPoint
instead of defining a new type?
试试这个:
class Point {
var x: Int
var y: Int
init(_ x: Int, _ y: Int) {
self.x = x
self.y = y
}
}
class Sprite {
private var privatePoint = Point(0,0)
var origin:Point{
get{
return privatePoint
}
}
}
所以你可以这样做:
var sprite = Sprite()
sprite.origin = Point(100, 200) // Error: setter is inaccessibie. Expected behavior.
sprite.origin.x = 150 // No error. The origin.x will be set to 150.
使origin
完全private
。唯一的 public 应该是 read-only 计算出来的 属性。您的 public incrementX
和 incrementY
方法仍将按预期工作,因为允许它们访问 origin
.
如果Point是一个结构体,那么这个read-only属性到returnorigin
就足够了,因为它将是一个副本:
var currentOrigin : Point {
return self.origin
}
但是,如果您坚持将 Point 设为 class 而不是结构,那么您就是在向您已经保留的同一个 Point 实例出售 reference,并且您无法阻止它可变。因此,您必须生产一份self.origin
您自己的副本,这样您就不会出售对您自己的私有实例的可变引用:
var currentOrigin : Point {
return Point(self.origin.x,self.origin.y)
}
(Objective-C Cocoa 通常解决这类问题的方法是实现 class 集群 of immutable/mutable对;例如,我们维护一个 NSMutableString 但我们出售一个 NSString 副本,这样我们的可变字符串就不会在我们背后发生变异。但是,我认为你拒绝使用结构是非常愚蠢的,因为这是 Swift 相对于 Objective-C 的巨大优势,即它解决了 您遇到的问题 。)
class Point {
private var x: Int
private var y: Int
init(_ x: Int, _ y: Int) {
self.x = x
self.y = y
}
func incrementX() {
x += 1
}
func incrementY() {
y += 1
}
func setX(_ value : Int){
x = value
}
func setY(_ value : Int){
y = value
}
}
class Sprite {
private (set) var origin = Point(0,0)
}
如果您需要获得 x
和 y
值,您应该在 Point
class 中添加 getter,如下所示:
func getX()->Int{
return x
}
所以你可以试试这个:
let sprite = Sprite()
sprite.origin.incrementX()
let point = Point(10,20)
point.setX(12)
point.setY(100)
所以你可以 origin
read-only,在我看来你必须设置 x
和 y
私有。
直接暴露点是不可能得到你想要的结果的,它是一个class,将通过引用传递。您可以使用协议仅公开您想要的 Point 部分。
protocol ReadablePoint {
var x: Int { get }
var y: Int { get }
}
class Point: ReadablePoint {
var x: Int
var y: Int
init(_ x: Int, _ y: Int) {
self.x = x
self.y = y
}
}
class Sprite {
private var _origin = Point(0,0)
var origin: ReadablePoint { return _origin }
func incrementX() {
_origin.x += 1
}
func incrementY() {
_origin.y += 1
}
}
这导致:
var sprite = Sprite()
sprite.origin = Point(100, 200) // error: cannot assign to property: 'origin' is a get-only property
sprite.origin.x = 150 // error: cannot assign to property: 'x' is a get-only property
我知道private(set)
如讨论的那样here and here,但它似乎不适用于我拥有具有内部属性的实例变量的情况。
我有一个实例 属性,我希望它具有只读访问权限。以下代码显示了一些具体示例。
class Point {
var x: Int
var y: Int
init(_ x: Int, _ y: Int) {
self.x = x
self.y = y
}
}
class Sprite {
private(set) var origin = Point(0,0)
func incrementX() {
origin.x += 1
}
func incrementY() {
origin.y += 1
}
}
在这里,我的意图是让Sprite
成为origin
的唯一所有者,允许它是唯一可以修改它的人,否则让origin
只读到public。然而:
var sprite = Sprite()
sprite.origin = Point(100, 200) // Error: setter is inaccessibie. Expected behavior.
sprite.origin.x = 150 // No error. The origin.x will be set to 150.
显然我仍然可以通过直接修改内部 x
和 y
来修改 origin
,但这不是我的意图。
如何使 origin
真正只读?我错过了什么吗?
将 Point
修改为 private var x: Int
或 fileprivate var x: Int
对我不起作用,因为我希望能够让外部 class 像 Sprite
修改 Point
.
编辑:从下面的评论来看,这不是我的实际情况,因为有人评论说我应该使用 CGPoint
。该代码作为示例使我的问题更加具体。我实际上在另一个 class 中有一个复杂的 class 作为实例 属性,而不是这个简单的 Point
。由于其他设计限制,我也不能将其用作 struct
。
Edit2:下面的回答指出您不能阻止引用可变。这是我刚刚学到的 Swift 的一个限制,因为显然,我可以使用 Point const &
:
class Point {
public:
int x;
int y;
Point(int _x, int _y) {
x = _x;
y = _y;
}
Point & operator=(const Point & rhs) {
x = rhs.x;
y = rhs.y;
return *this;
}
...
};
class Sprite {
Point origin;
public:
Sprite()
: origin(0,0)
{}
// HERE
Point const & getOrigin() const { return origin; }
void incrementX() { origin.x += 1; }
void incrementY() { origin.y += 1; }
};
int main(int argc, const char * argv[]) {
Sprite sprite = Sprite();
Point const & o = sprite.getOrigin();
cout << "o: " << o << endl; // (0,0)
// o.x = 10; // Error: Cannot assign to const-qualified type.
// o = Point (100, 200); // Error: no viable overlaod =.
sprite.incrementX();
cout << "o: " << o << endl; // (1,0). Swift can't get this behavior with `struct Point`, but it won't prevent some writing attempts with `class Point`.
cout << "sprite.origin: " << sprite.getOrigin() << endl; // (1,0).
return 0;
}
只需将 Point
设为结构体
struct Point {
var x: Int
var y: Int
init(_ x: Int, _ y: Int) {
self.x = x
self.y = y
}
}
class Sprite {
private(set) var origin = Point(0,0)
func incrementX() {
origin.x += 1
}
func incrementY() {
origin.y += 1
}
}
然后重试
var sprite = Sprite()
sprite.origin.x = 1
error: cannot assign to property: 'origin' setter is inaccessible
BTW: Why don't you simply use
CGPoint
instead of defining a new type?
试试这个:
class Point {
var x: Int
var y: Int
init(_ x: Int, _ y: Int) {
self.x = x
self.y = y
}
}
class Sprite {
private var privatePoint = Point(0,0)
var origin:Point{
get{
return privatePoint
}
}
}
所以你可以这样做:
var sprite = Sprite()
sprite.origin = Point(100, 200) // Error: setter is inaccessibie. Expected behavior.
sprite.origin.x = 150 // No error. The origin.x will be set to 150.
使origin
完全private
。唯一的 public 应该是 read-only 计算出来的 属性。您的 public incrementX
和 incrementY
方法仍将按预期工作,因为允许它们访问 origin
.
如果Point是一个结构体,那么这个read-only属性到returnorigin
就足够了,因为它将是一个副本:
var currentOrigin : Point {
return self.origin
}
但是,如果您坚持将 Point 设为 class 而不是结构,那么您就是在向您已经保留的同一个 Point 实例出售 reference,并且您无法阻止它可变。因此,您必须生产一份self.origin
您自己的副本,这样您就不会出售对您自己的私有实例的可变引用:
var currentOrigin : Point {
return Point(self.origin.x,self.origin.y)
}
(Objective-C Cocoa 通常解决这类问题的方法是实现 class 集群 of immutable/mutable对;例如,我们维护一个 NSMutableString 但我们出售一个 NSString 副本,这样我们的可变字符串就不会在我们背后发生变异。但是,我认为你拒绝使用结构是非常愚蠢的,因为这是 Swift 相对于 Objective-C 的巨大优势,即它解决了 您遇到的问题 。)
class Point {
private var x: Int
private var y: Int
init(_ x: Int, _ y: Int) {
self.x = x
self.y = y
}
func incrementX() {
x += 1
}
func incrementY() {
y += 1
}
func setX(_ value : Int){
x = value
}
func setY(_ value : Int){
y = value
}
}
class Sprite {
private (set) var origin = Point(0,0)
}
如果您需要获得 x
和 y
值,您应该在 Point
class 中添加 getter,如下所示:
func getX()->Int{
return x
}
所以你可以试试这个:
let sprite = Sprite()
sprite.origin.incrementX()
let point = Point(10,20)
point.setX(12)
point.setY(100)
所以你可以 origin
read-only,在我看来你必须设置 x
和 y
私有。
直接暴露点是不可能得到你想要的结果的,它是一个class,将通过引用传递。您可以使用协议仅公开您想要的 Point 部分。
protocol ReadablePoint {
var x: Int { get }
var y: Int { get }
}
class Point: ReadablePoint {
var x: Int
var y: Int
init(_ x: Int, _ y: Int) {
self.x = x
self.y = y
}
}
class Sprite {
private var _origin = Point(0,0)
var origin: ReadablePoint { return _origin }
func incrementX() {
_origin.x += 1
}
func incrementY() {
_origin.y += 1
}
}
这导致:
var sprite = Sprite()
sprite.origin = Point(100, 200) // error: cannot assign to property: 'origin' is a get-only property
sprite.origin.x = 150 // error: cannot assign to property: 'x' is a get-only property