Я пытаюсь понять что-то с новым языком 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
}
}
Но это может быть достигнуто другим, более коротким способом, как указано в разделе "Контроль доступа" "Язык быстрого программирования" :
public class Clock {
public private(set) var hours = 0
}
В качестве дополнительной заметки вы должны предоставить публичный инициализатор при объявлении публичного типа. Даже если вы предоставляете значения по умолчанию для всех свойств, init()
должен быть явно открытым:
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) не применяется так, как вы можете думать в Objective C. Люди могут изменять ваши переменные readonly напрямую, если они действительно этого хотят. Они просто не делают этого с открытым интерфейсом класса.
Вы можете сделать что-то подобное в 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, за исключением того, что фактические сохраненные свойства видны в открытом интерфейсе.
В быстром вы также можете сделать что-то более интересное, что вы также можете сделать в 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++
}
}