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.

显然我仍然可以通过直接修改内部 xy 来修改 origin,但这不是我的意图。

如何使 origin 真正只读?我错过了什么吗?

Point 修改为 private var x: Intfileprivate 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 incrementXincrementY 方法仍将按预期工作,因为允许它们访问 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)

}

如果您需要获得 xy 值,您应该在 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,在我看来你必须设置 xy 私有。

直接暴露点是不可能得到你想要的结果的,它是一个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