Я пытаюсь что-то выяснить с новым языком 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
Для определенной цели мы не можем напрямую касаться секунд, минут и часов, и разрешено только увеличивать секунду за секундой, используя метод. Только этот метод может изменить значения, используя прием переменных экземпляра.
Поскольку в 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++
}
}
}
}
Это бы сработало, но любой мог напрямую изменить свойства.
Возможно, у меня уже был плохой дизайн в Objective-C, и поэтому потенциальный новый Swift эквивалент не имеет смысла. Если нет и если у кого-то есть ответ, буду очень признателен;)
Может быть, будущее Механизмы контроля доступа обещанные Apple - это ответ?
Спасибо!
Просто добавьте префикс объявления свойства к private(set)
, вот так:
public private(set) var hours: UInt = 0
public private(set) var minutes: UInt = 0
public private(set) var seconds: UInt = 0
private
сохраняет его локальным для исходного файла, в то время как internal
сохраняет его локальным для модуля/проекта.
private(set)
создает свойство read-only
, в то время как private
устанавливает оба значения, set
и get
в private.
Есть два основных способа делать то, что вы хотите. Первый способ заключается в наличии частной собственности и публичного вычисляемого свойства, которое возвращает это свойство:
public class Clock {
private var _hours = 0
public var hours: UInt {
return _hours
}
}
Но это может быть достигнуто другим, более коротким способом, как указано в разделе "Контроль доступа" книги "The Swift Language" :
public class Clock {
public private(set) var hours = 0
}
Как примечание, вы ДОЛЖНЫ предоставить публичный инициализатор при объявлении публичного типа. Даже если вы предоставляете значения по умолчанию для всех свойств, init()
должен быть явно определен как public:
public class Clock {
public private(set) var hours = 0
public init() {
hours = 0
}
// or simply `public init() {}`
}
Это также объясняется в том же разделе книги, когда речь идет об инициализаторах по умолчанию.
Поскольку контроля доступа нет (это означает, что вы не можете заключить контракт на доступ, который отличается в зависимости от того, кто является вызывающим абонентом), вот что я сейчас сделаю:
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() }
}
Это просто добавляет некоторый уровень косвенности к прямому доступу к счетчику; другой класс может сделать часы, а затем получить доступ к их счетчику напрямую. Но идея - т.е. контракт, который мы пытаемся заключить, - это то, что другой класс должен использовать только свойства и методы верхнего уровня Clock. Мы не можем обеспечить этот контракт, но на самом деле это было практически невозможно реализовать в Objective-C.
На самом деле контроль доступа (который еще не существует в Swift) не такой строгий, как вы можете подумать в Задаче 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++
}
}
}
}
это то же самое, что и в 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++
}
}