objective-c swift properties

objective c - Propiedades de variables de solo lectura y no calculadas en Swift



objective-c properties (4)

Estoy intentando descubrir algo con el nuevo lenguaje de Apple Swift. Digamos que solía hacer algo como lo siguiente en Objective-C. Tengo propiedades de readonly , y no se pueden cambiar individualmente. Sin embargo, usando un método específico, las propiedades se cambian de una manera lógica.

Tomo el siguiente ejemplo, un reloj muy simple. Escribiría esto en 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

Para un propósito específico, no podemos tocar los segundos, minutos y horas directamente, y solo se puede aumentar de segundo a segundo usando un método. Solo este método podría cambiar los valores utilizando el truco de las variables de instancia.

Como no hay tales cosas en Swift, estoy tratando de encontrar un equivalente. Si hago esto:

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++ } } } }

Eso funcionaría, pero cualquiera podría cambiar directamente las propiedades.

Tal vez ya tenía un mal diseño en Objective-C y es por eso que el potencial nuevo equivalente de Swift no tiene sentido. Si no, y si alguien tiene una respuesta, estaría muy agradecido;)

Tal vez los futuros mecanismos de control de acceso prometidos por Apple es la respuesta?

¡Gracias!


Como no hay controles de acceso (lo que significa que no puede hacer un contrato de acceso que difiera dependiendo de quién sea el que llama), esto es lo que haría por ahora:

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() } }

Esto simplemente agrega un nivel de indirección, por así decirlo, para acceder directamente al mostrador; otra clase puede hacer un reloj y luego acceder a su contador directamente. Pero la idea , es decir, el contrato que intentamos hacer, es que otra clase debería usar solo las propiedades y métodos de nivel superior del reloj. No podemos hacer cumplir ese contrato, pero en realidad era bastante imposible de aplicar en Objective-C también.


En realidad, el control de acceso (que aún no existe en Swift) no se aplica tanto como se puede pensar en el Objetivo C. Las personas pueden modificar directamente las variables de solo lectura, si realmente lo desean. Simplemente no lo hacen con la interfaz pública de la clase.

Puedes hacer algo similar en Swift (cortar y pegar tu código, más algunas modificaciones, no lo probé):

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++ } } } }

que es igual a lo que tiene en el Objetivo C, excepto que las propiedades almacenadas reales son visibles en la interfaz pública.

Rápidamente también puede hacer algo más interesante, que también puede hacer en Objective C, pero probablemente sea más bonito a la velocidad (editado en el navegador, no lo probé):

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++ } }


Hay dos formas básicas de hacer lo que quieres. La primera forma es tener una propiedad privada y una propiedad pública computada que devuelve esa propiedad:

public class Clock { private var _hours = 0 public var hours: UInt { return _hours } }

Pero esto se puede lograr de una manera diferente, más corta, como se indica en la sección "Control de acceso" del libro "El lenguaje de programación Swift" :

public class Clock { public private(set) var hours = 0 }

Como nota al margen, DEBE proporcionar un inicializador público al declarar un tipo público. Incluso si proporciona valores predeterminados para todas las propiedades, init() debe definirse explícitamente como público:

public class Clock { public private(set) var hours = 0 public init() { hours = 0 } // or simply `public init() {}` }

Esto también se explica en la misma sección del libro, cuando se habla de inicializadores predeterminados.


Simplemente prefija la declaración de propiedad con private(set) , así:

public private(set) var hours: UInt = 0 public private(set) var minutes: UInt = 0 public private(set) var seconds: UInt = 0

private mantiene local en un archivo fuente, mientras que internal mantiene local en el módulo / proyecto.

private(set) crea una propiedad de read-only , mientras que private establece both, set y get to private.