2017-02-01 33 views
1

Я знаю private(set), как обсуждалось, here и here, но он не работает для моей ситуации, когда у меня есть переменная экземпляра с внутренними свойствами.Доступ только для чтения, например, свойство в Swift

У меня есть свойство экземпляра, где я бы хотел, чтобы он имел доступ только для чтения. Следующий код показывает конкретный пример.

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 только для чтения для общественности. Однако:

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 путем изменения внутренней x и y непосредственно, но это не мое намерение.

Как я могу сделать origin действительно доступным только для чтения? Я что-то упускаю?

Изменение Point к private var x: Int или fileprivate var x: Int не будет работать для меня, потому что я хочу быть в состоянии позволить внешний класс как Sprite изменить Point.

Редактировать: Из комментариев ниже, это не мой фактический сценарий, так как кто-то замечает, что я должен использовать CGPoint. Код служит примером, чтобы сделать мой вопрос более конкретным. У меня на самом деле сложный класс как свойство экземпляра в другом классе, а не этот простой Point. Я также не могу использовать его как struct из-за других ограничений конструкции.

Редактирование 2: Ответ ниже указывает на то, что ссылка не может быть изменена. Это ограничение в Swift я только что узнал, потому что, по-видимому, я могу достичь именно то, что я хотел в C++ с использованием 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; 
} 
+0

Это не является "класса собственности". Это свойство _instance_. – matt

ответ

1

origin сделать полностью private. Единственное публичное дело должно быть рассчитанным только для чтения ресурсом. Ваши общедоступные методы incrementX и incrementY будут по-прежнему работать по желанию, поскольку им разрешен доступ к origin.

Если точка является структурой, то достаточно для этого свойства только для чтения, чтобы вернуться origin, потому что это будет копия:

var currentOrigin : Point { 
    return self.origin 
} 

Но если вы настаиваете на том, точка класса, а не struct, то вы продаете ссылку с номером на тот же экземпляр Point, который вы уже сохраняете, а вы не можете помешать ему быть изменчивым.. Поэтому вам придется продукции копия self.originсебя, так что вы не вендинг изменяемой ссылки на свой собственный экземпляр:

var currentOrigin : Point { 
    return Point(self.origin.x,self.origin.y) 
} 

(Путь Objective-C какао обычно решает такого рода проблема заключается в реализации класса кластера классов из неизменяемых/изменяемых пар, например, мы поддерживаем NSMutableString, но мы продаем копию NSString, так что наша изменяемая строка не может быть изменена за нашей спиной. Однако я думаю, что вы очень глупы отказаться от использования структуры, поскольку это одно из огромных преимуществ Swift перед Objective-C, то есть оно решает t он очень проблеме вы имеете)

+0

Я решил принять ваш ответ, так как он ближе всего к тому, что я ищу. Возможно, функция языка, в которой можно хранить * ссылку * на экземпляр без возможности его модификации, пока не существует в Swift, насколько мне известно. Но пока, даже несмотря на то, что 'struct' не сохраняет копию обновленной по дизайну, она по-прежнему не будет облагать налогом производительность, так как Swift имеет оптимизацию под капотом для копирования только тогда, когда это абсолютно необходимо. – HuaTham

+0

Я также не согласен с тем, что я «глуп», когда у меня есть законный прецедент для настаивания на 'class' вместо' struct'. Как только я получаю копию из экземпляра 'struct', я, естественно, теряю все обновления, сделанные в оригинальной копии.Моя точка зрения (не каламбур) заключается в том, что я хочу иметь возможность ссылаться на один и тот же экземпляр, делая только определенного владельца этой ссылки, способной * читать и писать *. См. Пример C++ в моем обновленном OP. – HuaTham

+0

Способность C++ передавать указатель (чтобы сэкономить время/ресурсы), не допуская, чтобы получатель от мутации указала на вещь, конечно же круто. Разумеется, у Свифта нет ничего параллельного. Но, как я уже сказал, Point может быть структурой, и все же 'incrementX' и' incrementY' все еще работают точно так же, как написано. И есть способы прохождения по адресу структуры, чтобы получатель _can_ неявно записывал обратно в оригинал (например, 'inout'). Настоящая проблема здесь, мне кажется, в том, что заданный вами вопрос - это не вопрос, на который вы хотите ответить; ваш пример является непредставительным сокращением. – matt

3

Просто сделайте 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: Почему бы вам просто не использовать CGPoint вместо определения нового типа?

+0

@appzYorLife Спасибо за ответ. Мой код служит примером, чтобы сделать мой вопрос более конкретным. У меня на самом деле сложный класс как свойство в другом классе, а не в этом простом «Точке». Я также не могу использовать его как структуру из-за других ограничений конструкции. Однако знаете ли вы, почему Swift обрабатывает 'struct' и' class' по-другому в этом отношении? – HuaTham

+1

@HuaTham: Это потому, что структуры являются типами значений. Пожалуйста, прочитайте [this] (http://stackoverflow.com/help/someone-answers) и [это] (https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/ClassesAndStructures.html) –

0

попробовать это:

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. 
1
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 классе, как это:.

func getX()->Int{ 
    return x 
} 

так что вы можете попробовать это:

let sprite = Sprite() 
sprite.origin.incrementX() 

let point = Point(10,20) 
point.setX(12) 
point.setY(100) 

так что вы можете иметь origin только для чтения, на мой взгляд, вы должны установить x и y частный.

1

Невозможно получить желаемый результат, подвергая 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: PointProtocol { 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